diff options
Diffstat (limited to 'services')
56 files changed, 2745 insertions, 1099 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 69560e5..e9ac3f9 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -89,6 +89,12 @@ static const int kRecordThreadSleepUs = 5000; static const nsecs_t kSetParametersTimeout = seconds(2); +// minimum sleep time for the mixer thread loop when tracks are active but in underrun +static const uint32_t kMinThreadSleepTimeUs = 5000; +// maximum divider applied to the active sleep time in the mixer thread loop +static const uint32_t kMaxThreadSleepTimeShift = 2; + + // ---------------------------------------------------------------------------- static bool recordingAllowed() { @@ -1810,6 +1816,18 @@ audio_stream_t* AudioFlinger::PlaybackThread::stream() return &mOutput->stream->common; } +uint32_t AudioFlinger::PlaybackThread::activeSleepTimeUs() +{ + // A2DP output latency is not due only to buffering capacity. It also reflects encoding, + // decoding and transfer time. So sleeping for half of the latency would likely cause + // underruns + if (audio_is_a2dp_device((audio_devices_t)mDevice)) { + return (uint32_t)((uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000); + } else { + return (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2; + } +} + // ---------------------------------------------------------------------------- AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device) @@ -1846,6 +1864,7 @@ bool AudioFlinger::MixerThread::threadLoop() uint32_t activeSleepTime = activeSleepTimeUs(); uint32_t idleSleepTime = idleSleepTimeUs(); uint32_t sleepTime = idleSleepTime; + uint32_t sleepTimeShift = 0; Vector< sp<EffectChain> > effectChains; #ifdef DEBUG_CPU_USAGE ThreadCpuUsage cpu; @@ -1937,6 +1956,7 @@ bool AudioFlinger::MixerThread::threadLoop() standbyTime = systemTime() + kStandbyTimeInNsecs; sleepTime = idleSleepTime; + sleepTimeShift = 0; continue; } } @@ -1953,6 +1973,10 @@ bool AudioFlinger::MixerThread::threadLoop() // mix buffers... mAudioMixer->process(); sleepTime = 0; + // increase sleep time progressively when application underrun condition clears + if (sleepTimeShift > 0) { + sleepTimeShift--; + } standbyTime = systemTime() + kStandbyTimeInNsecs; //TODO: delay standby when effects have a tail } else { @@ -1960,7 +1984,17 @@ bool AudioFlinger::MixerThread::threadLoop() // buffer size, then write 0s to the output if (sleepTime == 0) { if (mixerStatus == MIXER_TRACKS_ENABLED) { - sleepTime = activeSleepTime; + sleepTime = activeSleepTime >> sleepTimeShift; + if (sleepTime < kMinThreadSleepTimeUs) { + sleepTime = kMinThreadSleepTimeUs; + } + // reduce sleep time in case of consecutive application underruns to avoid + // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer + // duration we would end up writing less data than needed by the audio HAL if + // the condition persists. + if (sleepTimeShift < kMaxThreadSleepTimeShift) { + sleepTimeShift++; + } } else { sleepTime = idleSleepTime; } @@ -2066,7 +2100,21 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track // The first time a track is added we wait // for all its buffers to be filled before processing it mAudioMixer->setActiveTrack(track->name()); - if (cblk->framesReady() && track->isReady() && + // make sure that we have enough frames to mix one full buffer. + // enforce this condition only once to enable draining the buffer in case the client + // app does not call stop() and relies on underrun to stop: + // hence the test on (track->mRetryCount >= kMaxTrackRetries) meaning the track was mixed + // during last round + uint32_t minFrames = 1; + if (!track->isStopped() && !track->isPausing() && + (track->mRetryCount >= kMaxTrackRetries)) { + if (t->sampleRate() == (int)mSampleRate) { + minFrames = mFrameCount; + } else { + minFrames = (mFrameCount * t->sampleRate()) / mSampleRate + 1; + } + } + if ((cblk->framesReady() >= minFrames) && track->isReady() && !track->isPaused() && !track->isTerminated()) { //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this); @@ -2386,11 +2434,6 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> return NO_ERROR; } -uint32_t AudioFlinger::MixerThread::activeSleepTimeUs() -{ - return (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2; -} - uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() { return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2; @@ -2857,7 +2900,7 @@ uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs() { uint32_t time; if (audio_is_linear_pcm(mFormat)) { - time = (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2; + time = PlaybackThread::activeSleepTimeUs(); } else { time = 10000; } @@ -7014,11 +7057,17 @@ void AudioFlinger::EffectHandle::dump(char* buffer, size_t size) AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread, int sessionId) - : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), + : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0), mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX), mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX) { mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC); + sp<ThreadBase> thread = mThread.promote(); + if (thread == 0) { + return; + } + mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) / + thread->frameCount(); } AudioFlinger::EffectChain::~EffectChain() @@ -7086,22 +7135,31 @@ void AudioFlinger::EffectChain::process_l() } bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) || (mSessionId == AUDIO_SESSION_OUTPUT_STAGE); - bool tracksOnSession = false; + // always process effects unless no more tracks are on the session and the effect tail + // has been rendered + bool doProcess = true; if (!isGlobalSession) { - tracksOnSession = (trackCnt() != 0); - } + bool tracksOnSession = (trackCnt() != 0); + + if (!tracksOnSession && mTailBufferCount == 0) { + doProcess = false; + } - // if no track is active, input buffer must be cleared here as the mixer process - // will not do it - if (tracksOnSession && - activeTrackCnt() == 0) { - size_t numSamples = thread->frameCount() * thread->channelCount(); - memset(mInBuffer, 0, numSamples * sizeof(int16_t)); + if (activeTrackCnt() == 0) { + // if no track is active and the effect tail has not been rendered, + // the input buffer must be cleared here as the mixer process will not do it + if (tracksOnSession || mTailBufferCount > 0) { + size_t numSamples = thread->frameCount() * thread->channelCount(); + memset(mInBuffer, 0, numSamples * sizeof(int16_t)); + if (mTailBufferCount > 0) { + mTailBufferCount--; + } + } + } } size_t size = mEffects.size(); - // do not process effect if no track is present in same audio session - if (isGlobalSession || tracksOnSession) { + if (doProcess) { for (size_t i = 0; i < size; i++) { mEffects[i]->process(); } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 4b794ef..6cafa7e 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -776,7 +776,7 @@ private: virtual int getTrackName_l() = 0; virtual void deleteTrackName_l(int name) = 0; - virtual uint32_t activeSleepTimeUs() = 0; + virtual uint32_t activeSleepTimeUs(); virtual uint32_t idleSleepTimeUs() = 0; virtual uint32_t suspendSleepTimeUs() = 0; @@ -833,7 +833,6 @@ private: Vector< sp<Track> > *tracksToRemove); virtual int getTrackName_l(); virtual void deleteTrackName_l(int name); - virtual uint32_t activeSleepTimeUs(); virtual uint32_t idleSleepTimeUs(); virtual uint32_t suspendSleepTimeUs(); @@ -1247,6 +1246,10 @@ private: // corresponding to a suspend all request. static const int kKeyForSuspendAll = 0; + // minimum duration during which we force calling effect process when last track on + // a session is stopped or removed to allow effect tail to be rendered + static const int kProcessTailDurationMs = 1000; + void process_l(); void lock() { @@ -1287,7 +1290,8 @@ private: void decTrackCnt() { android_atomic_dec(&mTrackCnt); } int32_t trackCnt() { return mTrackCnt;} - void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt); } + void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt); + mTailBufferCount = mMaxTailBuffers; } void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); } int32_t activeTrackCnt() { return mActiveTrackCnt;} @@ -1338,6 +1342,8 @@ private: int16_t *mOutBuffer; // chain output buffer volatile int32_t mActiveTrackCnt; // number of active tracks connected volatile int32_t mTrackCnt; // number of tracks connected + int32_t mTailBufferCount; // current effect tail buffer count + int32_t mMaxTailBuffers; // maximum effect tail buffers bool mOwnInBuffer; // true if the chain owns its input buffer int mVolumeCtrlIdx; // index of insert effect having control over volume uint32_t mLeftVolume; // previous volume on left channel diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 790b395..5289741 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -507,6 +507,15 @@ void EventHub::getVirtualKeyDefinitions(int32_t deviceId, } } +String8 EventHub::getKeyCharacterMapFile(int32_t deviceId) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device) { + return device->keyMap.keyCharacterMapFile; + } + return String8(); +} + EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { if (deviceId == 0) { deviceId = mBuiltInKeyboardId; @@ -1013,16 +1022,12 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { - // Set system properties for the keyboard. - setKeyboardPropertiesLocked(device, false); - // Register the keyboard as a built-in keyboard if it is eligible. if (!keyMapStatus && mBuiltInKeyboardId == -1 && isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { mBuiltInKeyboardId = device->id; - setKeyboardPropertiesLocked(device, true); } // 'Q' key support = cheap test of whether this is an alpha-capable kbd @@ -1120,17 +1125,6 @@ status_t EventHub::loadKeyMapLocked(Device* device) { return device->keyMap.load(device->identifier, device->configuration); } -void EventHub::setKeyboardPropertiesLocked(Device* device, bool builtInKeyboard) { - int32_t id = builtInKeyboard ? 0 : device->id; - android::setKeyboardProperties(id, device->identifier, - device->keyMap.keyLayoutFile, device->keyMap.keyCharacterMapFile); -} - -void EventHub::clearKeyboardPropertiesLocked(Device* device, bool builtInKeyboard) { - int32_t id = builtInKeyboard ? 0 : device->id; - android::clearKeyboardProperties(id); -} - bool EventHub::isExternalDeviceLocked(Device* device) { if (device->configuration) { bool value; @@ -1184,9 +1178,7 @@ void EventHub::closeDeviceLocked(Device* device) { LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", device->path.string(), mBuiltInKeyboardId); mBuiltInKeyboardId = -1; - clearKeyboardPropertiesLocked(device, true); } - clearKeyboardPropertiesLocked(device, false); if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) { LOGW("Could not remove device fd from epoll instance. errno=%d", errno); diff --git a/services/input/EventHub.h b/services/input/EventHub.h index d37549a..9d8252e 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -208,6 +208,8 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0; + virtual String8 getKeyCharacterMapFile(int32_t deviceId) const = 0; + /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */ virtual void requestReopenDevices() = 0; @@ -264,6 +266,8 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector<VirtualKeyDefinition>& outVirtualKeys) const; + virtual String8 getKeyCharacterMapFile(int32_t deviceId) const; + virtual void requestReopenDevices(); virtual void wake(); @@ -321,8 +325,6 @@ private: void loadConfigurationLocked(Device* device); status_t loadVirtualKeyMapLocked(Device* device); status_t loadKeyMapLocked(Device* device); - void setKeyboardPropertiesLocked(Device* device, bool builtInKeyboard); - void clearKeyboardPropertiesLocked(Device* device, bool builtInKeyboard); bool isExternalDeviceLocked(Device* device); diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 88c19a4..b34ff25 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -1775,6 +1775,7 @@ void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); info->setKeyboardType(mKeyboardType); + info->setKeyCharacterMapFile(getEventHub()->getKeyCharacterMapFile(getDeviceId())); } void KeyboardInputMapper::dump(String8& dump) { diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index a086208..08efe7d 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -609,6 +609,10 @@ private: } } + virtual String8 getKeyCharacterMapFile(int32_t deviceId) const { + return String8(); + } + virtual bool isExternal(int32_t deviceId) const { return false; } diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index eb75ebc..2af5103 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -751,10 +751,13 @@ class AppWidgetService extends IAppWidgetService.Stub return; } ArrayList<AppWidgetId> instances = p.instances; + final int callingUid = getCallingUid(); final int N = instances.size(); for (int i=0; i<N; i++) { AppWidgetId id = instances.get(i); - updateAppWidgetInstanceLocked(id, views); + if (canAccessAppWidgetId(id, callingUid)) { + updateAppWidgetInstanceLocked(id, views); + } } } } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 8c42f31..6e4aca7 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -26,8 +26,10 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import android.bluetooth.BluetoothTetheringDataTracker; import android.content.ContentResolver; import android.content.Context; +import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.DummyDataStateTracker; @@ -51,6 +53,7 @@ import android.net.Proxy; import android.net.ProxyProperties; import android.net.RouteInfo; import android.net.wifi.WifiStateTracker; +import android.net.wimax.WimaxManagerConstants; import android.os.Binder; import android.os.FileUtils; import android.os.Handler; @@ -78,10 +81,14 @@ import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; import com.google.android.collect.Lists; import com.google.android.collect.Sets; - +import dalvik.system.DexClassLoader; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.InvocationTargetException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -491,6 +498,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance(); mNetTrackers[netType].startMonitoring(context, mHandler); break; + case ConnectivityManager.TYPE_WIMAX: + mNetTrackers[netType] = makeWimaxStateTracker(); + if (mNetTrackers[netType]!= null) { + mNetTrackers[netType].startMonitoring(context, mHandler); + } + break; case ConnectivityManager.TYPE_ETHERNET: mNetTrackers[netType] = EthernetDataTracker.getInstance(); mNetTrackers[netType].startMonitoring(context, mHandler); @@ -501,7 +514,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { continue; } mCurrentLinkProperties[netType] = null; - if (mNetConfigs[netType].isDefault()) mNetTrackers[netType].reconnect(); + if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) { + mNetTrackers[netType].reconnect(); + } } IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); @@ -531,7 +546,81 @@ public class ConnectivityService extends IConnectivityManager.Stub { loadGlobalProxy(); } +private NetworkStateTracker makeWimaxStateTracker() { + //Initialize Wimax + DexClassLoader wimaxClassLoader; + Class wimaxStateTrackerClass = null; + Class wimaxServiceClass = null; + Class wimaxManagerClass; + String wimaxJarLocation; + String wimaxLibLocation; + String wimaxManagerClassName; + String wimaxServiceClassName; + String wimaxStateTrackerClassName; + + NetworkStateTracker wimaxStateTracker = null; + + boolean isWimaxEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_wimaxEnabled); + + if (isWimaxEnabled) { + try { + wimaxJarLocation = mContext.getResources().getString( + com.android.internal.R.string.config_wimaxServiceJarLocation); + wimaxLibLocation = mContext.getResources().getString( + com.android.internal.R.string.config_wimaxNativeLibLocation); + wimaxManagerClassName = mContext.getResources().getString( + com.android.internal.R.string.config_wimaxManagerClassname); + wimaxServiceClassName = mContext.getResources().getString( + com.android.internal.R.string.config_wimaxServiceClassname); + wimaxStateTrackerClassName = mContext.getResources().getString( + com.android.internal.R.string.config_wimaxStateTrackerClassname); + + log("wimaxJarLocation: " + wimaxJarLocation); + wimaxClassLoader = new DexClassLoader(wimaxJarLocation, + new ContextWrapper(mContext).getCacheDir().getAbsolutePath(), + wimaxLibLocation, ClassLoader.getSystemClassLoader()); + + try { + wimaxManagerClass = wimaxClassLoader.loadClass(wimaxManagerClassName); + wimaxStateTrackerClass = wimaxClassLoader.loadClass(wimaxStateTrackerClassName); + wimaxServiceClass = wimaxClassLoader.loadClass(wimaxServiceClassName); + } catch (ClassNotFoundException ex) { + loge("Exception finding Wimax classes: " + ex.toString()); + return null; + } + } catch(Resources.NotFoundException ex) { + loge("Wimax Resources does not exist!!! "); + return null; + } + + try { + log("Starting Wimax Service... "); + + Constructor wmxStTrkrConst = wimaxStateTrackerClass.getConstructor + (new Class[] {Context.class, Handler.class}); + wimaxStateTracker = (NetworkStateTracker)wmxStTrkrConst.newInstance(mContext, + mHandler); + + Constructor wmxSrvConst = wimaxServiceClass.getDeclaredConstructor + (new Class[] {Context.class, wimaxStateTrackerClass}); + wmxSrvConst.setAccessible(true); + IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(mContext, wimaxStateTracker); + wmxSrvConst.setAccessible(false); + + ServiceManager.addService(WimaxManagerConstants.WIMAX_SERVICE, svcInvoker); + } catch(Exception ex) { + loge("Exception creating Wimax classes: " + ex.toString()); + return null; + } + } else { + loge("Wimax is not enabled or not added to the network attributes!!! "); + return null; + } + + return wimaxStateTracker; + } /** * Sets the preferred network. * @param preference the new preference @@ -1508,6 +1597,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (checkType == prevNetType) continue; if (mNetConfigs[checkType] == null) continue; if (!mNetConfigs[checkType].isDefault()) continue; + if (mNetTrackers[checkType] == null) continue; // Enabling the isAvailable() optimization caused mobile to not get // selected if it was in the middle of error handling. Specifically diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java index d34087f..16eeb7b 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -163,7 +163,6 @@ public class DeviceStorageMonitorService extends Binder { } catch (IllegalArgumentException e) { // ignore; report -1 } - mCacheFileStats.restat(CACHE_PATH); EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT, mFreeMem, mFreeSystem, mFreeCache); } diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index dea9007..64789d3 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -82,7 +82,9 @@ class DockObserver extends UEventObserver { // Don't force screen on when undocking from the desk dock. // The change in power state will do this anyway. // FIXME - we should be configurable. - if (mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK || + if ((mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK + && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK + && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(), false, true); diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags index a7eff93..4dad209 100644 --- a/services/java/com/android/server/EventLogTags.logtags +++ b/services/java/com/android/server/EventLogTags.logtags @@ -50,12 +50,12 @@ option java_package com.android.server # NotificationManagerService.java # --------------------------- # when a NotificationManager.notify is called -2750 notification_enqueue (pkg|3),(id|1|5),(notification|3) +2750 notification_enqueue (pkg|3),(id|1|5),(tag|3),(notification|3) # when someone tries to cancel a notification, the notification manager sometimes # calls this with flags too -2751 notification_cancel (pkg|3),(id|1|5),(required_flags|1) +2751 notification_cancel (pkg|3),(id|1|5),(tag|3),(required_flags|1),(forbidden_flags|1) # when someone tries to cancel all of the notifications for a particular package -2752 notification_cancel_all (pkg|3),(required_flags|1) +2752 notification_cancel_all (pkg|3),(required_flags|1),(forbidden_flags|1) # --------------------------- @@ -142,5 +142,5 @@ option java_package com.android.server # --------------------------- # NetworkStatsService.java # --------------------------- -51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3) -51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3) +51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3),(dev_history_start|2|3) +51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3),(dev_history_start|2|3) diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 6ddbf5a..f5c4ed4 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -161,6 +161,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans = new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE); + // Used to bring IME service up to visible adjustment while it is being shown. + final ServiceConnection mVisibleConnection = new ServiceConnection() { + @Override public void onServiceConnected(ComponentName name, IBinder service) { + } + + @Override public void onServiceDisconnected(ComponentName name) { + } + }; + boolean mVisibleBound = false; + // Ongoing notification private NotificationManager mNotificationManager; private KeyguardManager mKeyguardManager; @@ -372,8 +382,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { mScreenOn = true; + refreshImeWindowVisibilityLocked(); } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; + setImeWindowVisibilityStatusHiddenLocked(); } else if (intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { hideInputMethodMenu(); return; @@ -468,8 +480,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Uh oh, current input method is no longer around! // Pick another one... Slog.i(TAG, "Current input method removed: " + curInputMethodId); - mImeWindowVis = 0; - updateImeWindowStatusLocked(); + setImeWindowVisibilityStatusHiddenLocked(); if (!chooseNewDefaultIMELocked()) { changed = true; curIm = null; @@ -493,11 +504,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - class MethodCallback extends IInputMethodCallback.Stub { - final IInputMethod mMethod; + private static class MethodCallback extends IInputMethodCallback.Stub { + private final IInputMethod mMethod; + private final InputMethodManagerService mParentIMMS; - MethodCallback(IInputMethod method) { + MethodCallback(final IInputMethod method, final InputMethodManagerService imms) { mMethod = method; + mParentIMMS = imms; } @Override @@ -506,7 +519,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void sessionCreated(IInputMethodSession session) throws RemoteException { - onSessionCreated(mMethod, session); + mParentIMMS.onSessionCreated(mMethod, session); } } @@ -623,7 +636,27 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - void updateImeWindowStatusLocked() { + private void setImeWindowVisibilityStatusHiddenLocked() { + mImeWindowVis = 0; + updateImeWindowStatusLocked(); + } + + private void refreshImeWindowVisibilityLocked() { + final Configuration conf = mRes.getConfiguration(); + final boolean haveHardKeyboard = conf.keyboard + != Configuration.KEYBOARD_NOKEYS; + final boolean hardKeyShown = haveHardKeyboard + && conf.hardKeyboardHidden + != Configuration.HARDKEYBOARDHIDDEN_YES; + final boolean isScreenLocked = mKeyguardManager != null + && mKeyguardManager.isKeyguardLocked() + && mKeyguardManager.isKeyguardSecure(); + mImeWindowVis = (!isScreenLocked && (mInputShown || hardKeyShown)) ? + (InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE) : 0; + updateImeWindowStatusLocked(); + } + + private void updateImeWindowStatusLocked() { setImeWindowStatus(mCurToken, mImeWindowVis, mBackDisposition); } @@ -837,7 +870,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_CREATE_SESSION, mCurMethod, - new MethodCallback(mCurMethod))); + new MethodCallback(mCurMethod, this))); } // Return to client, and we will get back with it when // we have had a session made for it. @@ -886,7 +919,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub com.android.internal.R.string.input_method_binding_label); mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); - if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) { + if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE + | Context.BIND_NOT_VISIBLE)) { mLastBindTime = SystemClock.uptimeMillis(); mHaveConnection = true; mCurId = info.getId(); @@ -943,7 +977,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + mCurClient); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_CREATE_SESSION, mCurMethod, - new MethodCallback(mCurMethod))); + new MethodCallback(mCurMethod, this))); } } } @@ -968,6 +1002,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } void unbindCurrentMethodLocked(boolean reportToClient) { + if (mVisibleBound) { + mContext.unbindService(mVisibleConnection); + mVisibleBound = false; + } + if (mHaveConnection) { mContext.unbindService(this); mHaveConnection = false; @@ -997,8 +1036,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub sessionState.session.finishSession(); } catch (RemoteException e) { Slog.w(TAG, "Session failed to close due to remote exception", e); - mImeWindowVis = 0; - updateImeWindowStatusLocked(); + setImeWindowVisibilityStatusHiddenLocked(); } } } @@ -1263,16 +1301,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mCurMethod != null) { try { - final Configuration conf = mRes.getConfiguration(); - final boolean haveHardKeyboard = conf.keyboard - != Configuration.KEYBOARD_NOKEYS; - final boolean hardKeyShown = haveHardKeyboard - && conf.hardKeyboardHidden - != Configuration.HARDKEYBOARDHIDDEN_YES; - mImeWindowVis = (mInputShown || hardKeyShown) ? ( - InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE) - : 0; - updateImeWindowStatusLocked(); + refreshImeWindowVisibilityLocked(); // If subtype is null, try to find the most applicable one from // getCurrentInputMethodSubtype. if (subtype == null) { @@ -1360,6 +1389,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod, resultReceiver)); mInputShown = true; + if (mHaveConnection && !mVisibleBound) { + mContext.bindService(mCurIntent, mVisibleConnection, Context.BIND_AUTO_CREATE); + mVisibleBound = true; + } res = true; } else if (mHaveConnection && SystemClock.uptimeMillis() >= (mLastBindTime+TIME_TO_RECONNECT)) { @@ -1371,7 +1404,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub SystemClock.uptimeMillis()-mLastBindTime,1); Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()"); mContext.unbindService(this); - mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE); + mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE + | Context.BIND_NOT_VISIBLE); } return res; @@ -1393,13 +1427,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!mIWindowManager.inputMethodClientHasFocus(client)) { if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); - mImeWindowVis = 0; - updateImeWindowStatusLocked(); + setImeWindowVisibilityStatusHiddenLocked(); return false; } } catch (RemoteException e) { - mImeWindowVis = 0; - updateImeWindowStatusLocked(); + setImeWindowVisibilityStatusHiddenLocked(); return false; } } @@ -1432,6 +1464,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else { res = false; } + if (mHaveConnection && mVisibleBound) { + mContext.unbindService(mVisibleConnection); + mVisibleBound = false; + } mInputShown = false; mShowRequested = false; mShowExplicitlyRequested = false; diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 4c26dbb..75e5366 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -247,6 +247,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub * Notify our observers of an interface removal. */ private void notifyInterfaceRemoved(String iface) { + // netd already clears out quota and alerts for removed ifaces; update + // our sanity-checking state. + mActiveAlertIfaces.remove(iface); + mActiveQuotaIfaces.remove(iface); + for (INetworkManagementEventObserver obs : mObservers) { try { obs.interfaceRemoved(iface); @@ -851,13 +856,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub NetworkInterface internalNetworkInterface = NetworkInterface.getByName(internalInterface); - Collection<InterfaceAddress>interfaceAddresses = - internalNetworkInterface.getInterfaceAddresses(); - cmd += " " + interfaceAddresses.size(); - for (InterfaceAddress ia : interfaceAddresses) { - InetAddress addr = NetworkUtils.getNetworkPart(ia.getAddress(), - ia.getNetworkPrefixLength()); - cmd = cmd + " " + addr.getHostAddress() + "/" + ia.getNetworkPrefixLength(); + if (internalNetworkInterface == null) { + cmd += " 0"; + } else { + Collection<InterfaceAddress>interfaceAddresses = + internalNetworkInterface.getInterfaceAddresses(); + cmd += " " + interfaceAddresses.size(); + for (InterfaceAddress ia : interfaceAddresses) { + InetAddress addr = NetworkUtils.getNetworkPart(ia.getAddress(), + ia.getNetworkPrefixLength()); + cmd = cmd + " " + addr.getHostAddress() + "/" + ia.getNetworkPrefixLength(); + } } mConnector.doCommand(cmd); @@ -1131,12 +1140,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub final StringBuilder command = new StringBuilder(); command.append("bandwidth removeiquota ").append(iface); + mActiveQuotaIfaces.remove(iface); + mActiveAlertIfaces.remove(iface); + try { // TODO: support quota shared across interfaces mConnector.doCommand(command.toString()); - mActiveQuotaIfaces.remove(iface); - mActiveAlertIfaces.remove(iface); } catch (NativeDaemonConnectorException e) { + // TODO: include current iptables state throw new IllegalStateException("Error communicating to native daemon", e); } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 7d1d976..5039294 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -707,7 +707,8 @@ public class NotificationManagerService extends INotificationManager.Stub // behalf of the download manager without affecting other apps. if (!pkg.equals("com.android.providers.downloads") || Log.isLoggable("DownloadManager", Log.VERBOSE)) { - EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString()); + EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, + notification.toString()); } if (pkg == null || notification == null) { @@ -944,7 +945,8 @@ public class NotificationManagerService extends INotificationManager.Stub */ private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, int mustNotHaveFlags, boolean sendDelete) { - EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags); + EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, + mustHaveFlags, mustNotHaveFlags); synchronized (mNotificationList) { int index = indexOfNotificationLocked(pkg, tag, id); @@ -972,7 +974,8 @@ public class NotificationManagerService extends INotificationManager.Stub */ boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, int mustNotHaveFlags, boolean doit) { - EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags); + EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags, + mustNotHaveFlags); synchronized (mNotificationList) { final int N = mNotificationList.size(); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index fdae4bd..2a0d2a0 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -100,10 +100,13 @@ public class PowerManagerService extends IPowerManager.Stub private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec private static final int LONG_DIM_TIME = 7000; // t+N-5 sec - // How long to wait to debounce light sensor changes. + // How long to wait to debounce light sensor changes in milliseconds private static final int LIGHT_SENSOR_DELAY = 2000; - // For debouncing the proximity sensor. + // light sensor events rate in microseconds + private static final int LIGHT_SENSOR_RATE = 1000000; + + // For debouncing the proximity sensor in milliseconds private static final int PROXIMITY_SENSOR_DELAY = 1000; // trigger proximity if distance is less than 5 cm @@ -1686,6 +1689,11 @@ public class PowerManagerService extends IPowerManager.Stub // before showing it to the user. We want the light off // until it is ready to be shown to the user, not it using // whatever the last value it had. + if (DEBUG_SCREEN_ON) { + Slog.i(TAG, "Forcing brightness 0: mPowerState=0x" + + Integer.toHexString(mPowerState) + + " mSkippedScreenOn=" + mSkippedScreenOn); + } mScreenBrightness.forceValueLocked(Power.BRIGHTNESS_OFF); } } @@ -2035,12 +2043,14 @@ public class PowerManagerService extends IPowerManager.Stub } finally { Binder.restoreCallingIdentity(identity); } - mScreenBrightness.setTargetLocked(brightness, steps, - INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue); - if (DEBUG_SCREEN_ON) { - RuntimeException e = new RuntimeException("here"); - e.fillInStackTrace(); - Slog.i(TAG, "Setting screen brightness: " + brightness, e); + if (!mSkippedScreenOn) { + mScreenBrightness.setTargetLocked(brightness, steps, + INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue); + if (DEBUG_SCREEN_ON) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(TAG, "Setting screen brightness: " + brightness, e); + } } } @@ -2083,6 +2093,11 @@ public class PowerManagerService extends IPowerManager.Stub ? LightsService.BRIGHTNESS_MODE_SENSOR : LightsService.BRIGHTNESS_MODE_USER); if ((mask & SCREEN_BRIGHT_BIT) != 0) { + if (DEBUG_SCREEN_ON) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(TAG, "Set LCD brightness: " + value, e); + } mLcdLight.setBrightness(value, brightnessMode); } if ((mask & BUTTON_BRIGHT_BIT) != 0) { @@ -2134,7 +2149,7 @@ public class PowerManagerService extends IPowerManager.Stub delta = (targetValue - (nominalCurrentValue >= 0 ? nominalCurrentValue : curValue)) / stepsToTarget; - if (mSpew) { + if (mSpew || DEBUG_SCREEN_ON) { String noticeMe = nominalCurrentValue == curValue ? "" : " ******************"; Slog.i(TAG, "setTargetLocked mask=" + mask + " curValue=" + curValue + " target=" + target + " targetValue=" + targetValue + " delta=" + delta @@ -2198,25 +2213,21 @@ public class PowerManagerService extends IPowerManager.Stub } public void run() { - if (mAnimateScreenLights) { - synchronized (mLocks) { + synchronized (mLocks) { + // we're turning off + final boolean turningOff = animating && targetValue == Power.BRIGHTNESS_OFF; + if (mAnimateScreenLights || !turningOff) { long now = SystemClock.uptimeMillis(); boolean more = mScreenBrightness.stepLocked(); if (more) { mScreenOffHandler.postAtTime(this, now+(1000/60)); } - } - } else { - synchronized (mLocks) { - // we're turning off - final boolean animate = animating && targetValue == Power.BRIGHTNESS_OFF; - if (animate) { - // It's pretty scary to hold mLocks for this long, and we should - // redesign this, but it works for now. - nativeStartSurfaceFlingerAnimation( - mScreenOffReason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR - ? 0 : mAnimationSetting); - } + } else { + // It's pretty scary to hold mLocks for this long, and we should + // redesign this, but it works for now. + nativeStartSurfaceFlingerAnimation( + mScreenOffReason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR + ? 0 : mAnimationSetting); mScreenBrightness.jumpToTargetLocked(); } } @@ -2528,8 +2539,10 @@ public class PowerManagerService extends IPowerManager.Stub } if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) { - mScreenBrightness.setTargetLocked(lcdValue, AUTOBRIGHTNESS_ANIM_STEPS, - INITIAL_SCREEN_BRIGHTNESS, (int)mScreenBrightness.curValue); + if (!mSkippedScreenOn) { + mScreenBrightness.setTargetLocked(lcdValue, AUTOBRIGHTNESS_ANIM_STEPS, + INITIAL_SCREEN_BRIGHTNESS, (int)mScreenBrightness.curValue); + } } if (mButtonBrightnessOverride < 0) { mButtonLight.setBrightness(buttonValue); @@ -3053,7 +3066,7 @@ public class PowerManagerService extends IPowerManager.Stub try { if (enable) { mSensorManager.registerListener(mLightListener, mLightSensor, - SensorManager.SENSOR_DELAY_NORMAL); + LIGHT_SENSOR_RATE); } else { mSensorManager.unregisterListener(mLightListener); mHandler.removeCallbacks(mAutoBrightnessTask); diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java index 373b11c..8384ebc 100644 --- a/services/java/com/android/server/TextServicesManagerService.java +++ b/services/java/com/android/server/TextServicesManagerService.java @@ -40,6 +40,8 @@ import android.provider.Settings; import android.service.textservice.SpellCheckerService; import android.text.TextUtils; import android.util.Slog; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; import android.view.textservice.SpellCheckerInfo; import android.view.textservice.SpellCheckerSubtype; @@ -222,20 +224,40 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { if (hashCode == 0 && !allowImplicitlySelectedSubtype) { return null; } - final String systemLocale = - mContext.getResources().getConfiguration().locale.toString(); + String candidateLocale = null; + if (hashCode == 0) { + // Spell checker language settings == "auto" + final InputMethodManager imm = + (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + final InputMethodSubtype currentInputMethodSubtype = + imm.getCurrentInputMethodSubtype(); + if (currentInputMethodSubtype != null) { + final String localeString = currentInputMethodSubtype.getLocale(); + if (!TextUtils.isEmpty(localeString)) { + // 1. Use keyboard locale if available in the spell checker + candidateLocale = localeString; + } + } + } + if (candidateLocale == null) { + // 2. Use System locale if available in the spell checker + candidateLocale = mContext.getResources().getConfiguration().locale.toString(); + } + } SpellCheckerSubtype candidate = null; for (int i = 0; i < sci.getSubtypeCount(); ++i) { final SpellCheckerSubtype scs = sci.getSubtypeAt(i); if (hashCode == 0) { - if (systemLocale.equals(locale)) { + if (candidateLocale.equals(locale)) { return scs; } else if (candidate == null) { final String scsLocale = scs.getLocale(); - if (systemLocale.length() >= 2 + if (candidateLocale.length() >= 2 && scsLocale.length() >= 2 - && systemLocale.substring(0, 2).equals( + && candidateLocale.substring(0, 2).equals( scsLocale.substring(0, 2))) { + // Fall back to the applicable language candidate = scs; } } @@ -244,9 +266,13 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale + ", " + scs.getLocale()); } + // 3. Use the user specified spell check language return scs; } } + // 4. Fall back to the applicable language and return it if not null + // 5. Simply just return it even if it's null which means we could find no suitable + // spell check languages return candidate; } } @@ -366,9 +392,16 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { Slog.d(TAG, "FinishSpellCheckerService"); } synchronized(mSpellCheckerMap) { + final ArrayList<SpellCheckerBindGroup> removeList = + new ArrayList<SpellCheckerBindGroup>(); for (SpellCheckerBindGroup group : mSpellCheckerBindGroups.values()) { if (group == null) continue; - group.removeListener(listener); + // Use removeList to avoid modifying mSpellCheckerBindGroups in this loop. + removeList.add(group); + } + final int removeSize = removeList.size(); + for (int i = 0; i < removeSize; ++i) { + removeList.get(i).removeListener(listener); } } } @@ -643,6 +676,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } + // cleanLocked may remove elements from mSpellCheckerBindGroups private void cleanLocked() { if (DBG) { Slog.d(TAG, "cleanLocked"); diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index 431cc39..c7fbc00 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -63,6 +63,10 @@ class UiModeManagerService extends IUiModeManager.Stub { private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL"; + // Enable launching of applications when entering the dock. + private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true; + private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true; + private static final int MSG_UPDATE_TWILIGHT = 0; private static final int MSG_ENABLE_LOCATION_UPDATES = 1; private static final int MSG_GET_NEW_LOCATION_UPDATE = 2; @@ -123,6 +127,10 @@ class UiModeManagerService extends IUiModeManager.Stub { @Override public void onReceive(Context context, Intent intent) { if (getResultCode() != Activity.RESULT_OK) { + if (LOG) { + Slog.v(TAG, "Handling broadcast result for action " + intent.getAction() + + ": canceled: " + getResultCode()); + } return; } @@ -135,14 +143,16 @@ class UiModeManagerService extends IUiModeManager.Stub { if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { // Only launch car home when car mode is enabled and the caller // has asked us to switch to it. - if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + if (ENABLE_LAUNCH_CAR_DOCK_APP + && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { category = Intent.CATEGORY_CAR_DOCK; } } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) { // Only launch car home when desk mode is enabled and the caller // has asked us to switch to it. Currently re-using the car // mode flag since we don't have a formal API for "desk mode". - if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + if (ENABLE_LAUNCH_DESK_DOCK_APP + && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { category = Intent.CATEGORY_DESK_DOCK; } } else { @@ -151,6 +161,12 @@ class UiModeManagerService extends IUiModeManager.Stub { category = Intent.CATEGORY_HOME; } } + + if (LOG) { + Slog.v(TAG, String.format( + "Handling broadcast result for action %s: enable=0x%08x disable=0x%08x category=%s", + intent.getAction(), enableFlags, disableFlags, category)); + } if (category != null) { // This is the new activity that will serve as home while @@ -424,11 +440,22 @@ class UiModeManagerService extends IUiModeManager.Stub { } } + final static boolean isDeskDockState(int state) { + switch (state) { + case Intent.EXTRA_DOCK_STATE_DESK: + case Intent.EXTRA_DOCK_STATE_LE_DESK: + case Intent.EXTRA_DOCK_STATE_HE_DESK: + return true; + default: + return false; + } + } + final void updateConfigurationLocked(boolean sendIt) { int uiMode = Configuration.UI_MODE_TYPE_NORMAL; if (mCarModeEnabled) { uiMode = Configuration.UI_MODE_TYPE_CAR; - } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { + } else if (isDeskDockState(mDockState)) { uiMode = Configuration.UI_MODE_TYPE_DESK; } if (mCarModeEnabled) { @@ -477,7 +504,7 @@ class UiModeManagerService extends IUiModeManager.Stub { if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { adjustStatusBarCarModeLocked(); oldAction = UiModeManager.ACTION_EXIT_CAR_MODE; - } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) { + } else if (isDeskDockState(mLastBroadcastState)) { oldAction = UiModeManager.ACTION_EXIT_DESK_MODE; } @@ -491,12 +518,12 @@ class UiModeManagerService extends IUiModeManager.Stub { mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR; action = UiModeManager.ACTION_ENTER_CAR_MODE; } - } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { - if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_DESK) { + } else if (isDeskDockState(mDockState)) { + if (!isDeskDockState(mLastBroadcastState)) { if (oldAction != null) { mContext.sendBroadcast(new Intent(oldAction)); } - mLastBroadcastState = Intent.EXTRA_DOCK_STATE_DESK; + mLastBroadcastState = mDockState; action = UiModeManager.ACTION_ENTER_DESK_MODE; } } else { @@ -505,6 +532,12 @@ class UiModeManagerService extends IUiModeManager.Stub { } if (action != null) { + if (LOG) { + Slog.v(TAG, String.format( + "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x", + action, enableFlags, disableFlags)); + } + // Send the ordered broadcast; the result receiver will receive after all // broadcasts have been sent. If any broadcast receiver changes the result // code from the initial value of RESULT_OK, then the result receiver will @@ -523,11 +556,13 @@ class UiModeManagerService extends IUiModeManager.Stub { } else { Intent homeIntent = null; if (mCarModeEnabled) { - if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + if (ENABLE_LAUNCH_CAR_DOCK_APP + && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { homeIntent = buildHomeIntent(Intent.CATEGORY_CAR_DOCK); } - } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { - if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + } else if (isDeskDockState(mDockState)) { + if (ENABLE_LAUNCH_DESK_DOCK_APP + && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { homeIntent = buildHomeIntent(Intent.CATEGORY_DESK_DOCK); } } else { @@ -535,6 +570,12 @@ class UiModeManagerService extends IUiModeManager.Stub { homeIntent = buildHomeIntent(Intent.CATEGORY_HOME); } } + + if (LOG) { + Slog.v(TAG, "updateLocked: null action, mDockState=" + + mDockState +", firing homeIntent: " + homeIntent); + } + if (homeIntent != null) { try { mContext.startActivity(homeIntent); diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 7fa404e..4925a4e 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -476,6 +476,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub { ParcelFileDescriptor updateWallpaperBitmapLocked(String name) { if (name == null) name = ""; try { + if (!WALLPAPER_DIR.exists()) { + WALLPAPER_DIR.mkdir(); + FileUtils.setPermissions( + WALLPAPER_DIR.getPath(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, + -1, -1); + } ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE, MODE_CREATE|MODE_READ_WRITE); mName = name; diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 2d3ac00..728fb26 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -451,7 +451,8 @@ public class Watchdog extends Thread { Thread dropboxThread = new Thread("watchdogWriteToDropbox") { public void run() { mActivity.addErrorToDropBox( - "watchdog", null, null, null, name, null, stack, null); + "watchdog", null, "system_server", null, null, + name, null, stack, null); } }; dropboxThread.start(); diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 5bfe6f8..3c65255 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -619,12 +619,7 @@ public class WifiService extends IWifiManager.Stub { */ public WifiConfiguration getWifiApConfiguration() { enforceAccessPermission(); - if (mWifiStateMachineChannel != null) { - return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel); - } else { - Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); - return null; - } + return mWifiStateMachine.syncGetWifiApConfiguration(); } /** @@ -921,18 +916,14 @@ public class WifiService extends IWifiManager.Stub { Slog.d(TAG, "ACTION_SCREEN_ON"); } mAlarmManager.cancel(mIdleIntent); - mDeviceIdle = false; mScreenOff = false; - // 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.enableBackgroundScanCommand(false); } mWifiStateMachine.enableAllNetworks(); - updateWifiState(); + setDeviceIdleAndUpdateWifi(false); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { if (DBG) { Slog.d(TAG, "ACTION_SCREEN_OFF"); @@ -950,36 +941,17 @@ public class WifiService extends IWifiManager.Stub { * or plugged in to AC). */ if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) { - WifiInfo info = mWifiStateMachine.syncRequestConnectionInfo(); - if (info.getSupplicantState() != SupplicantState.COMPLETED) { - // we used to go to sleep immediately, but this caused some race conditions - // we don't have time to track down for this release. Delay instead, - // but not as long as we would if connected (below) - // TODO - fix the race conditions and switch back to the immediate turn-off - long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min - if (DBG) { - Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms"); - } - mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); - // // do not keep Wifi awake when screen is off if Wifi is not associated - // mDeviceIdle = true; - // updateWifiState(); + //Delayed shutdown if wifi is connected + if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) { + if (DBG) Slog.d(TAG, "setting ACTION_DEVICE_IDLE: " + idleMillis + " ms"); + mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + + idleMillis, mIdleIntent); } else { - long triggerTime = System.currentTimeMillis() + idleMillis; - if (DBG) { - Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis - + "ms"); - } - mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); + setDeviceIdleAndUpdateWifi(true); } } } else if (action.equals(ACTION_DEVICE_IDLE)) { - if (DBG) { - Slog.d(TAG, "got ACTION_DEVICE_IDLE"); - } - mDeviceIdle = true; - reportStartWorkSource(); - updateWifiState(); + setDeviceIdleAndUpdateWifi(true); } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { /* * Set a timer to put Wi-Fi to sleep, but only if the screen is off @@ -1056,6 +1028,12 @@ public class WifiService extends IWifiManager.Stub { } }; + private void setDeviceIdleAndUpdateWifi(boolean deviceIdle) { + mDeviceIdle = deviceIdle; + reportStartWorkSource(); + updateWifiState(); + } + private synchronized void reportStartWorkSource() { mTmpWorkSource.clear(); if (mDeviceIdle) { diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index fd528cc..b70ed96 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -115,8 +115,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>(); - private final SparseArray<IAccessibilityInteractionConnection> mWindowIdToInteractionConnectionMap = - new SparseArray<IAccessibilityInteractionConnection>(); + private final SparseArray<AccessibilityConnectionWrapper> mWindowIdToInteractionConnectionWrapperMap = + new SparseArray<AccessibilityConnectionWrapper>(); private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>(); @@ -439,16 +439,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final IWindow addedWindowToken = windowToken; final IAccessibilityInteractionConnection addedConnection = connection; final int windowId = sNextWindowId++; - addedConnection.asBinder().linkToDeath(new DeathRecipient() { - public void binderDied() { - synchronized (mLock) { - addedConnection.asBinder().unlinkToDeath(this, 0); - removeAccessibilityInteractionConnection(addedWindowToken); - } - } - }, 0); + AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(windowId, + connection); + wrapper.linkToDeath(); mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder()); - mWindowIdToInteractionConnectionMap.put(windowId, connection); + mWindowIdToInteractionConnectionWrapperMap.put(windowId, wrapper); if (DEBUG) { Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId); } @@ -462,18 +457,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = 0; i < count; i++) { if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) { final int windowId = mWindowIdToWindowTokenMap.keyAt(i); - mWindowIdToWindowTokenMap.remove(windowId); - mWindowIdToInteractionConnectionMap.remove(windowId); - if (DEBUG) { - Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId); - } + AccessibilityConnectionWrapper wrapper = + mWindowIdToInteractionConnectionWrapperMap.get(windowId); + wrapper.unlinkToDeath(); + removeAccessibilityInteractionConnectionLocked(windowId); return; } } } } - public IAccessibilityServiceConnection registerEventListener(IEventListener listener) { + public void registerEventListener(IEventListener listener) { mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT, FUNCTION_REGISTER_EVENT_LISTENER); ComponentName componentName = new ComponentName("foo.bar", @@ -501,7 +495,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; Service service = new Service(componentName, accessibilityServiceInfo, true); service.onServiceConnected(componentName, listener.asBinder()); - return service; + } + + /** + * Removes an AccessibilityInteractionConnection. + * + * @param windowId The id of the window to which the connection is targeted. + */ + private void removeAccessibilityInteractionConnectionLocked(int windowId) { + mWindowIdToWindowTokenMap.remove(windowId); + mWindowIdToInteractionConnectionWrapperMap.remove(windowId); + if (DEBUG) { + Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId); + } } /** @@ -594,6 +600,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ private void notifyEventListenerLocked(Service service, int eventType) { IEventListener listener = service.mServiceInterface; + + // If the service died/was disabled while the message for dispatching + // the accessibility event was propagating the listener may be null. + if (listener == null) { + return; + } + AccessibilityEvent event = service.mPendingEvents.get(eventType); // Check for null here because there is a concurrent scenario in which this @@ -618,7 +631,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub service.mPendingEvents.remove(eventType); try { if (mSecurityPolicy.canRetrieveWindowContent(service)) { - event.setConnection(service); + event.setConnectionId(service.mId); } else { event.setSource(null); } @@ -666,6 +679,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mComponentNameToServiceMap.remove(service.mComponentName); mHandler.removeMessages(service.mId); service.unlinkToOwnDeath(); + service.dispose(); updateInputFilterLocked(); return removed; } @@ -895,6 +909,33 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub sendStateToClientsLocked(); } + private class AccessibilityConnectionWrapper implements DeathRecipient { + private final int mWindowId; + private final IAccessibilityInteractionConnection mConnection; + + public AccessibilityConnectionWrapper(int windowId, + IAccessibilityInteractionConnection connection) { + mWindowId = windowId; + mConnection = connection; + } + + public void linkToDeath() throws RemoteException { + mConnection.asBinder().linkToDeath(this, 0); + } + + public void unlinkToDeath() { + mConnection.asBinder().unlinkToDeath(this, 0); + } + + @Override + public void binderDied() { + unlinkToDeath(); + synchronized (mLock) { + removeAccessibilityInteractionConnectionLocked(mWindowId); + } + } + } + /** * This class represents an accessibility service. It stores all per service * data required for the service management, provides API for starting/stopping the @@ -997,7 +1038,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (!mIsAutomation) { mContext.unbindService(this); } - mService = null; return true; } return false; @@ -1021,7 +1061,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mService = service; mServiceInterface = IEventListener.Stub.asInterface(service); try { - mServiceInterface.setConnection(this); + mServiceInterface.setConnection(this, mId); synchronized (mLock) { tryAddServiceLocked(this); } @@ -1123,14 +1163,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (!permissionGranted) { return 0; } else { - connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId); - if (connection == null) { + AccessibilityConnectionWrapper wrapper = + mWindowIdToInteractionConnectionWrapperMap.get(accessibilityWindowId); + if (wrapper == null) { if (DEBUG) { Slog.e(LOG_TAG, "No interaction connection to window: " + accessibilityWindowId); } return 0; } + connection = wrapper.mConnection; } } final int interrogatingPid = Binder.getCallingPid(); @@ -1159,14 +1201,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (!permissionGranted) { return false; } else { - connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId); - if (connection == null) { + AccessibilityConnectionWrapper wrapper = + mWindowIdToInteractionConnectionWrapperMap.get(accessibilityWindowId); + if (wrapper == null) { if (DEBUG) { Slog.e(LOG_TAG, "No interaction connection to window: " + accessibilityWindowId); } return false; } + connection = wrapper.mConnection; } } final int interrogatingPid = Binder.getCallingPid(); @@ -1197,9 +1241,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mService.unlinkToDeath(this, 0); } + public void dispose() { + try { + // Clear the proxy in the other process so this + // IAccessibilityServiceConnection can be garbage collected. + mServiceInterface.setConnection(null, mId); + } catch (RemoteException re) { + /* ignore */ + } + mService = null; + mServiceInterface = null; + } + public void binderDied() { synchronized (mLock) { - mService.unlinkToDeath(this, 0); + unlinkToOwnDeath(); tryRemoveServiceLocked(this); // We no longer have an automation service, so restore // the state based on values in the settings database. @@ -1214,7 +1270,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (DEBUG) { Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); } - return mWindowIdToInteractionConnectionMap.get(windowId); + AccessibilityConnectionWrapper wrapper = + mWindowIdToInteractionConnectionWrapperMap.get(windowId); + return (wrapper != null) ? wrapper.mConnection : null; } private float getCompatibilityScale(int windowId) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 2a867af..df58e83 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -129,7 +129,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; @@ -296,7 +295,7 @@ public final class ActivityManagerService extends ActivityManagerNative /** * Historical data of past broadcasts, for debugging. */ - static final int MAX_BROADCAST_HISTORY = 100; + static final int MAX_BROADCAST_HISTORY = 25; final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY]; @@ -411,6 +410,17 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord mHomeProcess; /** + * This is the process holding the activity the user last visited that + * is in a different process from the one they are currently in. + */ + ProcessRecord mPreviousProcess; + + /** + * The time at which the previous process was last visible. + */ + long mPreviousProcessVisibleTime; + + /** * Packages that the user has asked to have run in screen size * compatibility mode instead of filling the screen. */ @@ -724,6 +734,13 @@ public final class ActivityManagerService extends ActivityManagerNative int mLruSeq = 0; /** + * Keep track of the number of service processes we last found, to + * determine on the next iteration which should be B services. + */ + int mNumServiceProcs = 0; + int mNewNumServiceProcs = 0; + + /** * System monitoring: number of processes that died since the last * N procs were started. */ @@ -1198,8 +1215,8 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized (ActivityManagerService.this) { long now = SystemClock.uptimeMillis(); - if (now < (mLastMemUsageReportTime+10000)) { - // Don't report more than every 10 seconds to somewhat + if (now < (mLastMemUsageReportTime+5*60*1000)) { + // Don't report more than every 5 minutes to somewhat // avoid spamming. return; } @@ -1207,6 +1224,25 @@ public final class ActivityManagerService extends ActivityManagerNative } Thread thread = new Thread() { @Override public void run() { + StringBuilder dropBuilder = new StringBuilder(1024); + StringBuilder logBuilder = new StringBuilder(1024); + StringWriter oomSw = new StringWriter(); + PrintWriter oomPw = new PrintWriter(oomSw); + StringWriter catSw = new StringWriter(); + PrintWriter catPw = new PrintWriter(catSw); + String[] emptyArgs = new String[] { }; + StringBuilder tag = new StringBuilder(128); + StringBuilder stack = new StringBuilder(128); + tag.append("Low on memory -- "); + dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw, + tag, stack); + dropBuilder.append(stack); + dropBuilder.append('\n'); + dropBuilder.append('\n'); + String oomString = oomSw.toString(); + dropBuilder.append(oomString); + dropBuilder.append('\n'); + logBuilder.append(oomString); try { java.lang.Process proc = Runtime.getRuntime().exec(new String[] { "procrank", }); @@ -1220,16 +1256,27 @@ public final class ActivityManagerService extends ActivityManagerNative break; } if (line.length() > 0) { - Slog.i(TAG, line); + logBuilder.append(line); + logBuilder.append('\n'); } + dropBuilder.append(line); + dropBuilder.append('\n'); } converter.close(); } catch (IOException e) { } - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - dumpApplicationMemoryUsage(null, pw, " ", new String[] { }, true); - Slog.i(TAG, sw.toString()); + synchronized (ActivityManagerService.this) { + catPw.println(); + dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); + catPw.println(); + dumpServicesLocked(null, catPw, emptyArgs, 0, false, false, null); + catPw.println(); + dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); + } + dropBuilder.append(catSw.toString()); + addErrorToDropBox("lowmem", null, "system_server", null, + null, tag.toString(), dropBuilder.toString(), null, null); + Slog.i(TAG, logBuilder.toString()); synchronized (ActivityManagerService.this) { long now = SystemClock.uptimeMillis(); if (mLastMemUsageReportTime < now) { @@ -1381,7 +1428,8 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false); + mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, + false, null, null, null); } } @@ -1451,6 +1499,7 @@ public final class ActivityManagerService extends ActivityManagerNative mConfiguration.setToDefaults(); mConfiguration.locale = Locale.getDefault(); + mConfigurationSeq = mConfiguration.seq = 1; mProcessStats.init(); mCompatModePackages = new CompatModePackages(this, systemDir); @@ -1641,7 +1690,9 @@ public final class ActivityManagerService extends ActivityManagerNative final void setFocusedActivityLocked(ActivityRecord r) { if (mFocusedActivity != r) { mFocusedActivity = r; - mWindowManager.setFocusedApp(r, true); + if (r != null) { + mWindowManager.setFocusedApp(r.appToken, true); + } } } @@ -2318,7 +2369,8 @@ public final class ActivityManagerService extends ActivityManagerNative // XXX we are not dealing with propagating grantedUriPermissions... // those are not yet exposed to user code, so there is no need. int res = mMainStack.startActivityLocked(r.app.thread, intent, - r.resolvedType, null, 0, aInfo, resultTo, resultWho, + r.resolvedType, null, 0, aInfo, + resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1, r.launchedFromUid, false, false, null); Binder.restoreCallingIdentity(origId); @@ -2401,13 +2453,13 @@ public final class ActivityManagerService extends ActivityManagerNative return; } final long origId = Binder.clearCallingIdentity(); - mWindowManager.setAppOrientation(r, requestedOrientation); + mWindowManager.setAppOrientation(r.appToken, requestedOrientation); Configuration config = mWindowManager.updateOrientationFromAppTokens( mConfiguration, - r.mayFreezeScreenLocked(r.app) ? r : null); + r.mayFreezeScreenLocked(r.app) ? r.appToken : null); if (config != null) { r.frozenBeforeDestroy = true; - if (!updateConfigurationLocked(config, r, false)) { + if (!updateConfigurationLocked(config, r, false, false)) { mMainStack.resumeTopActivityLocked(null); } } @@ -2421,7 +2473,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (r == null) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - return mWindowManager.getAppOrientation(r); + return mWindowManager.getAppOrientation(r.appToken); } } @@ -2487,7 +2539,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<activities.size(); i++) { ActivityRecord r = activities.get(i); if (!r.finishing) { - int index = mMainStack.indexOfTokenLocked(r); + int index = mMainStack.indexOfTokenLocked(r.appToken); if (index >= 0) { mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED, null, "finish-heavy"); @@ -2589,7 +2641,7 @@ public final class ActivityManagerService extends ActivityManagerNative int i; for (i=mMainStack.mHistory.size()-1; i>=0; i--) { ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - if (r == token) { + if (r.appToken == token) { return true; } if (r.fullscreen && !r.finishing) { @@ -2673,13 +2725,17 @@ public final class ActivityManagerService extends ActivityManagerNative } if (!r.finishing) { Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); + EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, + System.identityHashCode(r), + r.task.taskId, r.shortComponentName, + "proc died without state saved"); } r.makeFinishing(); mMainStack.mHistory.remove(i); r.takeFromHistory(); - mWindowManager.removeAppToken(r); + mWindowManager.removeAppToken(r.appToken); if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mMainStack.mHistory); + mMainStack.validateAppTokensLocked(); } r.removeUriPermissionsLocked(); @@ -3034,7 +3090,8 @@ public final class ActivityManagerService extends ActivityManagerNative Process.sendSignal(app.pid, Process.SIGNAL_QUIT); } - addErrorToDropBox("anr", app, activity, parent, annotation, cpuInfo, tracesFile, null); + addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, + cpuInfo, tracesFile, null); if (mController != null) { try { @@ -3178,7 +3235,49 @@ public final class ActivityManagerService extends ActivityManagerNative return; } killPackageProcessesLocked(packageName, pkgUid, - ProcessList.SECONDARY_SERVER_ADJ, false, true, true, false); + ProcessList.SERVICE_ADJ, false, true, true, false, "kill background"); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + public void killAllBackgroundProcesses() { + if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: killAllBackgroundProcesses() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + + long callingId = Binder.clearCallingIdentity(); + try { + synchronized(this) { + ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); + for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { + final int NA = apps.size(); + for (int ia=0; ia<NA; ia++) { + ProcessRecord app = apps.valueAt(ia); + if (app.persistent) { + // we don't kill persistent processes + continue; + } + if (app.removed) { + procs.add(app); + } else if (app.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { + app.removed = true; + procs.add(app); + } + } + } + + int N = procs.size(); + for (int i=0; i<N; i++) { + removeProcessLocked(procs.get(i), false, true, "kill all background"); + } } } finally { Binder.restoreCallingIdentity(callingId); @@ -3350,7 +3449,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean killPackageProcessesLocked(String packageName, int uid, int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit, - boolean evenPersistent) { + boolean evenPersistent, String reason) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // Remove all processes this package may have touched: all with the @@ -3385,7 +3484,7 @@ public final class ActivityManagerService extends ActivityManagerNative int N = procs.size(); for (int i=0; i<N; i++) { - removeProcessLocked(procs.get(i), callerWillRestart, allowRestart); + removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); } return N > 0; } @@ -3416,7 +3515,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean didSomething = killPackageProcessesLocked(name, uid, -100, - callerWillRestart, false, doit, evenPersistent); + callerWillRestart, false, doit, evenPersistent, "force stop"); TaskRecord lastTask = null; for (i=0; i<mMainStack.mHistory.size(); i++) { @@ -3504,11 +3603,11 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean removeProcessLocked(ProcessRecord app, - boolean callerWillRestart, boolean allowRestart) { + boolean callerWillRestart, boolean allowRestart, String reason) { final String name = app.processName; final int uid = app.info.uid; if (DEBUG_PROCESSES) Slog.d( - TAG, "Force removing process " + app + " (" + name + TAG, "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")"); mProcessNames.remove(name, uid); @@ -3523,9 +3622,10 @@ public final class ActivityManagerService extends ActivityManagerNative mPidsSelfLocked.remove(pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } + Slog.i(TAG, "Killing proc " + app.toShortString() + ": " + reason); handleAppDiedLocked(app, true, allowRestart); mLruProcesses.remove(app); - Process.killProcess(pid); + Process.killProcessQuiet(pid); if (app.persistent) { if (!callerWillRestart) { @@ -3724,7 +3824,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.instrumentationClass, profileFile, profileFd, profileAutoStop, app.instrumentationArguments, app.instrumentationWatcher, testMode, isRestrictedBackupMode || !normalMode, app.persistent, - mConfiguration, app.compat, getCommonServicesLocked(), + new Configuration(mConfiguration), app.compat, getCommonServicesLocked(), mCoreSettingsObserver.getCoreSettingsLocked()); updateLruProcessLocked(app, false, true); app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); @@ -4977,7 +5077,7 @@ public final class ActivityManagerService extends ActivityManagerNative outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((hiddenAppMem-homeAppMem)/2)); outInfo.hiddenAppThreshold = hiddenAppMem; outInfo.secondaryServerThreshold = mProcessList.getMemLevel( - ProcessList.SECONDARY_SERVER_ADJ); + ProcessList.SERVICE_ADJ); outInfo.visibleAppThreshold = mProcessList.getMemLevel( ProcessList.VISIBLE_APP_ADJ); outInfo.foregroundAppThreshold = mProcessList.getMemLevel( @@ -5101,10 +5201,10 @@ public final class ActivityManagerService extends ActivityManagerNative if (topThumbnail != null) { if (localLOGV) Slog.v(TAG, "Requesting top thumbnail"); try { - topThumbnail.requestThumbnail(topRecord); + topThumbnail.requestThumbnail(topRecord.appToken); } catch (Exception e) { Slog.w(TAG, "Exception thrown when requesting thumbnail", e); - sendPendingThumbnail(null, topRecord, null, null, true); + sendPendingThumbnail(null, topRecord.appToken, null, null, true); } } @@ -5475,7 +5575,7 @@ public final class ActivityManagerService extends ActivityManagerNative TaskRecord lastTask = null; for (int i=0; i<N; i++) { ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - if (r == token) { + if (r.appToken == token) { if (!onlyRoot || lastTask != r.task) { return r.task.taskId; } @@ -5496,7 +5596,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N; i++) { ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r.realActivity.equals(className) - && r != token && lastTask != r.task) { + && r.appToken != token && lastTask != r.task) { if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "others")) { i--; @@ -6168,7 +6268,7 @@ public final class ActivityManagerService extends ActivityManagerNative if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) { app.persistent = true; - app.maxAdj = ProcessList.CORE_SERVER_ADJ; + app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ; } if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); @@ -6566,14 +6666,15 @@ public final class ActivityManagerService extends ActivityManagerNative // If the worst oom_adj is somewhere in the hidden proc LRU range, // then constrain it so we will kill all hidden procs. - if (worstType < ProcessList.EMPTY_APP_ADJ && worstType > ProcessList.HIDDEN_APP_MIN_ADJ) { + if (worstType < ProcessList.HIDDEN_APP_MAX_ADJ + && worstType > ProcessList.HIDDEN_APP_MIN_ADJ) { worstType = ProcessList.HIDDEN_APP_MIN_ADJ; } // If this is not a secure call, don't let it kill processes that // are important. - if (!secure && worstType < ProcessList.SECONDARY_SERVER_ADJ) { - worstType = ProcessList.SECONDARY_SERVER_ADJ; + if (!secure && worstType < ProcessList.SERVICE_ADJ) { + worstType = ProcessList.SERVICE_ADJ; } Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType); @@ -6633,8 +6734,7 @@ public final class ActivityManagerService extends ActivityManagerNative mAlwaysFinishActivities = alwaysFinishActivities; // This happens before any activities are started, so we can // change mConfiguration in-place. - mConfiguration.updateFrom(configuration); - mConfigurationSeq = mConfiguration.seq = 1; + updateConfigurationLocked(configuration, null, false, true); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Initial config: " + mConfiguration); } } @@ -6831,7 +6931,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=procsToKill.size()-1; i>=0; i--) { ProcessRecord proc = procsToKill.get(i); Slog.i(TAG, "Removing system update proc: " + proc); - removeProcessLocked(proc, true, false); + removeProcessLocked(proc, true, false, "system update done"); } } @@ -7027,7 +7127,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Don't let services in this process be restarted and potentially // annoy the user repeatedly. Unless it is persistent, since those // processes run critical code. - removeProcessLocked(app, false, false); + removeProcessLocked(app, false, false, "crash"); mMainStack.resumeTopActivityLocked(null); return false; } @@ -7039,7 +7139,7 @@ public final class ActivityManagerService extends ActivityManagerNative // process, then terminate it to avoid getting in a loop. Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - int index = mMainStack.indexOfTokenLocked(r); + int index = mMainStack.indexOfActivityLocked(r); r.stack.finishActivityLocked(r, index, Activity.RESULT_CANCELED, null, "crashed"); // Also terminate any activities below it that aren't yet @@ -7138,16 +7238,18 @@ public final class ActivityManagerService extends ActivityManagerNative */ public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) { ProcessRecord r = findAppProcess(app, "Crash"); + final String processName = app == null ? "system_server" + : (r == null ? "unknown" : r.processName); EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(), - app == null ? "system" : (r == null ? "unknown" : r.processName), + processName, r == null ? -1 : r.info.flags, crashInfo.exceptionClassName, crashInfo.exceptionMessage, crashInfo.throwFileName, crashInfo.throwLineNumber); - addErrorToDropBox("crash", r, null, null, null, null, null, crashInfo); + addErrorToDropBox("crash", r, processName, null, null, null, null, null, crashInfo); crashApplication(r, crashInfo); } @@ -7220,6 +7322,7 @@ public final class ActivityManagerService extends ActivityManagerNative final boolean isSystemApp = process == null || (process.info.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0; + final String processName = process == null ? "unknown" : process.processName; final String dropboxTag = isSystemApp ? "system_app_strictmode" : "data_app_strictmode"; final DropBoxManager dbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); @@ -7232,7 +7335,7 @@ public final class ActivityManagerService extends ActivityManagerNative final StringBuilder sb = isSystemApp ? mStrictModeBuffer : new StringBuilder(1024); synchronized (sb) { bufferWasEmpty = sb.length() == 0; - appendDropBoxProcessHeaders(process, sb); + appendDropBoxProcessHeaders(process, processName, sb); sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); sb.append("System-App: ").append(isSystemApp).append("\n"); sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n"); @@ -7334,13 +7437,15 @@ public final class ActivityManagerService extends ActivityManagerNative public boolean handleApplicationWtf(IBinder app, String tag, ApplicationErrorReport.CrashInfo crashInfo) { ProcessRecord r = findAppProcess(app, "WTF"); + final String processName = app == null ? "system_server" + : (r == null ? "unknown" : r.processName); EventLog.writeEvent(EventLogTags.AM_WTF, Binder.getCallingPid(), - app == null ? "system" : (r == null ? "unknown" : r.processName), + processName, r == null ? -1 : r.info.flags, tag, crashInfo.exceptionMessage); - addErrorToDropBox("wtf", r, null, null, tag, null, null, crashInfo); + addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo); if (r != null && r.pid != Process.myPid() && Settings.Secure.getInt(mContext.getContentResolver(), @@ -7383,7 +7488,8 @@ public final class ActivityManagerService extends ActivityManagerNative * Utility function for addErrorToDropBox and handleStrictModeViolation's logging * to append various headers to the dropbox log text. */ - private void appendDropBoxProcessHeaders(ProcessRecord process, StringBuilder sb) { + private void appendDropBoxProcessHeaders(ProcessRecord process, String processName, + StringBuilder sb) { // Watchdog thread ends up invoking this function (with // a null ProcessRecord) to add the stack file to dropbox. // Do not acquire a lock on this (am) in such cases, as it @@ -7391,18 +7497,14 @@ public final class ActivityManagerService extends ActivityManagerNative // is invoked due to unavailability of lock on am and it // would prevent watchdog from killing system_server. if (process == null) { - sb.append("Process: system_server\n"); + sb.append("Process: ").append(processName).append("\n"); return; } // Note: ProcessRecord 'process' is guarded by the service // instance. (notably process.pkgList, which could otherwise change // concurrently during execution of this method) synchronized (this) { - if (process.pid == MY_PID) { - sb.append("Process: system_server\n"); - } else { - sb.append("Process: ").append(process.processName).append("\n"); - } + sb.append("Process: ").append(processName).append("\n"); int flags = process.info.flags; IPackageManager pm = AppGlobals.getPackageManager(); sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n"); @@ -7446,7 +7548,8 @@ public final class ActivityManagerService extends ActivityManagerNative * @param crashInfo giving an application stack trace, null if absent */ public void addErrorToDropBox(String eventType, - ProcessRecord process, ActivityRecord activity, ActivityRecord parent, String subject, + ProcessRecord process, String processName, ActivityRecord activity, + ActivityRecord parent, String subject, final String report, final File logFile, final ApplicationErrorReport.CrashInfo crashInfo) { // NOTE -- this must never acquire the ActivityManagerService lock, @@ -7460,7 +7563,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; final StringBuilder sb = new StringBuilder(1024); - appendDropBoxProcessHeaders(process, sb); + appendDropBoxProcessHeaders(process, processName, sb); if (activity != null) { sb.append("Activity: ").append(activity.shortComponentName).append("\n"); } @@ -7709,19 +7812,19 @@ public final class ActivityManagerService extends ActivityManagerNative } static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) { - if (adj >= ProcessList.EMPTY_APP_ADJ) { - return ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY; - } else if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) { + if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) { if (currApp != null) { currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1; } return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; + } else if (adj >= ProcessList.SERVICE_B_ADJ) { + return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; } else if (adj >= ProcessList.HOME_APP_ADJ) { if (currApp != null) { currApp.lru = 0; } return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; - } else if (adj >= ProcessList.SECONDARY_SERVER_ADJ) { + } else if (adj >= ProcessList.SERVICE_ADJ) { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE; @@ -7819,6 +7922,7 @@ public final class ActivityManagerService extends ActivityManagerNative boolean dumpAll = false; boolean dumpClient = false; + String dumpPackage = null; int opti = 0; while (opti < args.length) { @@ -7836,13 +7940,14 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" [-a] [-c] [-h] [cmd] ..."); pw.println(" cmd may be one of:"); pw.println(" a[ctivities]: activity stack state"); - pw.println(" b[roadcasts]: broadcast state"); - pw.println(" i[ntents]: pending intent state"); - pw.println(" p[rocesses]: process state"); + pw.println(" b[roadcasts] [PACKAGE_NAME]: broadcast state"); + pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state"); + pw.println(" p[rocesses] [PACKAGE_NAME]: process state"); pw.println(" o[om]: out of memory management"); pw.println(" prov[iders] [COMP_SPEC ...]: content provider state"); pw.println(" s[ervices] [COMP_SPEC ...]: service state"); pw.println(" service [COMP_SPEC]: service client-side state"); + pw.println(" package [PACKAGE_NAME]: all state related to given package"); pw.println(" all: dump all activities"); pw.println(" top: dump the top activity"); pw.println(" cmd may also be a COMP_SPEC to dump activities."); @@ -7863,22 +7968,58 @@ public final class ActivityManagerService extends ActivityManagerNative opti++; if ("activities".equals(cmd) || "a".equals(cmd)) { synchronized (this) { - dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient); + dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, null); } return; } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) { + String[] newArgs; + String name; + if (opti >= args.length) { + name = null; + newArgs = EMPTY_STRING_ARRAY; + } else { + name = args[opti]; + opti++; + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); + } synchronized (this) { - dumpBroadcastsLocked(fd, pw, args, opti, true); + dumpBroadcastsLocked(fd, pw, args, opti, true, name); } return; } else if ("intents".equals(cmd) || "i".equals(cmd)) { + String[] newArgs; + String name; + if (opti >= args.length) { + name = null; + newArgs = EMPTY_STRING_ARRAY; + } else { + name = args[opti]; + opti++; + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); + } synchronized (this) { - dumpPendingIntentsLocked(fd, pw, args, opti, true); + dumpPendingIntentsLocked(fd, pw, args, opti, true, name); } return; } else if ("processes".equals(cmd) || "p".equals(cmd)) { + String[] newArgs; + String name; + if (opti >= args.length) { + name = null; + newArgs = EMPTY_STRING_ARRAY; + } else { + name = args[opti]; + opti++; + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); + } synchronized (this) { - dumpProcessesLocked(fd, pw, args, opti, true); + dumpProcessesLocked(fd, pw, args, opti, true, name); } return; } else if ("oom".equals(cmd) || "o".equals(cmd)) { @@ -7888,7 +8029,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } else if ("providers".equals(cmd) || "prov".equals(cmd)) { synchronized (this) { - dumpProvidersLocked(fd, pw, args, opti, true); + dumpProvidersLocked(fd, pw, args, opti, true, null); } return; } else if ("service".equals(cmd)) { @@ -7901,16 +8042,32 @@ public final class ActivityManagerService extends ActivityManagerNative name = args[opti]; opti++; newArgs = new String[args.length - opti]; - if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); } if (!dumpService(fd, pw, name, newArgs, 0, dumpAll)) { pw.println("No services match: " + name); pw.println("Use -h for help."); } return; + } else if ("package".equals(cmd)) { + String[] newArgs; + if (opti >= args.length) { + pw.println("package: no package name specified"); + pw.println("Use -h for help."); + return; + } else { + dumpPackage = args[opti]; + opti++; + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); + args = newArgs; + opti = 0; + } } else if ("services".equals(cmd) || "s".equals(cmd)) { synchronized (this) { - dumpServicesLocked(fd, pw, args, opti, true, dumpClient); + dumpServicesLocked(fd, pw, args, opti, true, dumpClient, null); } return; } else { @@ -7926,76 +8083,78 @@ public final class ActivityManagerService extends ActivityManagerNative // No piece of data specified, dump everything. synchronized (this) { boolean needSep; - needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll); + needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpBroadcastsLocked(fd, pw, args, opti, dumpAll); + needSep = dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll); + needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient); + needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient); + needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpProcessesLocked(fd, pw, args, opti, dumpAll); + dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage); } } boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, boolean dumpClient) { + int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)"); pw.println(" Main stack:"); - dumpHistoryList(fd, pw, mMainStack.mHistory, " ", "Hist", true, !dumpAll, dumpClient); + dumpHistoryList(fd, pw, mMainStack.mHistory, " ", "Hist", true, !dumpAll, dumpClient, + dumpPackage); pw.println(" "); pw.println(" Running activities (most recent first):"); - dumpHistoryList(fd, pw, mMainStack.mLRUActivities, " ", "Run", false, !dumpAll, false); + dumpHistoryList(fd, pw, mMainStack.mLRUActivities, " ", "Run", false, !dumpAll, false, + dumpPackage); if (mMainStack.mWaitingVisibleActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting for another to become visible:"); dumpHistoryList(fd, pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false, - !dumpAll, false); + !dumpAll, false, dumpPackage); } if (mMainStack.mStoppingActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to stop:"); dumpHistoryList(fd, pw, mMainStack.mStoppingActivities, " ", "Stop", false, - !dumpAll, false); + !dumpAll, false, dumpPackage); } if (mMainStack.mGoingToSleepActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to sleep:"); dumpHistoryList(fd, pw, mMainStack.mGoingToSleepActivities, " ", "Sleep", false, - !dumpAll, false); + !dumpAll, false, dumpPackage); } if (mMainStack.mFinishingActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to finish:"); dumpHistoryList(fd, pw, mMainStack.mFinishingActivities, " ", "Fin", false, - !dumpAll, false); + !dumpAll, false, dumpPackage); } pw.println(" "); @@ -8018,6 +8177,12 @@ public final class ActivityManagerService extends ActivityManagerNative final int N = mRecentTasks.size(); for (int i=0; i<N; i++) { TaskRecord tr = mRecentTasks.get(i); + if (dumpPackage != null) { + if (tr.realActivity == null || + !dumpPackage.equals(tr.realActivity)) { + continue; + } + } pw.print(" * Recent #"); pw.print(i); pw.print(": "); pw.println(tr); if (dumpAll) { @@ -8035,7 +8200,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll) { + int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; int numPers = 0; @@ -8045,11 +8210,14 @@ public final class ActivityManagerService extends ActivityManagerNative for (SparseArray<ProcessRecord> procs : mProcessNames.getMap().values()) { final int NA = procs.size(); for (int ia=0; ia<NA; ia++) { + ProcessRecord r = procs.valueAt(ia); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } if (!needSep) { pw.println(" All known processes:"); needSep = true; } - ProcessRecord r = procs.valueAt(ia); pw.print(r.persistent ? " *PERS*" : " *APP*"); pw.print(" UID "); pw.print(procs.keyAt(ia)); pw.print(" "); pw.println(r); @@ -8066,31 +8234,49 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Process LRU list (sorted by oom_adj):"); dumpProcessOomList(pw, this, mLruProcesses, " ", - "Proc", "PERS", false); + "Proc", "PERS", false, dumpPackage); needSep = true; } if (dumpAll) { synchronized (mPidsSelfLocked) { - if (mPidsSelfLocked.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" PID mappings:"); - for (int i=0; i<mPidsSelfLocked.size(); i++) { - pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i)); - pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i)); + boolean printed = false; + for (int i=0; i<mPidsSelfLocked.size(); i++) { + ProcessRecord r = mPidsSelfLocked.valueAt(i); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" PID mappings:"); + printed = true; } + pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i)); + pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i)); } } } if (mForegroundProcesses.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Foreground Processes:"); - for (int i=0; i<mForegroundProcesses.size(); i++) { - pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i)); - pw.print(": "); pw.println(mForegroundProcesses.valueAt(i)); + synchronized (mPidsSelfLocked) { + boolean printed = false; + for (int i=0; i<mForegroundProcesses.size(); i++) { + ProcessRecord r = mPidsSelfLocked.get( + mForegroundProcesses.valueAt(i).pid); + if (dumpPackage != null && (r == null + || !dumpPackage.equals(r.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Foreground Processes:"); + printed = true; + } + pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i)); + pw.print(": "); pw.println(mForegroundProcesses.valueAt(i)); + } } } @@ -8099,7 +8285,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Persisent processes that are starting:"); dumpProcessList(pw, this, mPersistentStartingProcesses, " ", - "Starting Norm", "Restarting PERS"); + "Starting Norm", "Restarting PERS", dumpPackage); } if (mRemovedProcesses.size() > 0) { @@ -8107,7 +8293,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Processes that are being removed:"); dumpProcessList(pw, this, mRemovedProcesses, " ", - "Removed Norm", "Removed PERS"); + "Removed Norm", "Removed PERS", dumpPackage); } if (mProcessesOnHold.size() > 0) { @@ -8115,23 +8301,34 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Processes that are on old until the system is ready:"); dumpProcessList(pw, this, mProcessesOnHold, " ", - "OnHold Norm", "OnHold PERS"); + "OnHold Norm", "OnHold PERS", dumpPackage); } - needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll); + needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, dumpPackage); if (mProcessCrashTimes.getMap().size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Time since processes crashed:"); + boolean printed = false; long now = SystemClock.uptimeMillis(); for (Map.Entry<String, SparseArray<Long>> procs : mProcessCrashTimes.getMap().entrySet()) { + String pname = procs.getKey(); SparseArray<Long> uids = procs.getValue(); final int N = uids.size(); for (int i=0; i<N; i++) { - pw.print(" Process "); pw.print(procs.getKey()); - pw.print(" uid "); pw.print(uids.keyAt(i)); + int puid = uids.keyAt(i); + ProcessRecord r = mProcessNames.get(pname, puid); + if (dumpPackage != null && (r == null + || !dumpPackage.equals(r.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Time since processes crashed:"); + printed = true; + } + pw.print(" Process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); pw.print(": last crashed "); pw.print((now-uids.valueAt(i))); pw.println(" ms ago"); @@ -8140,16 +8337,26 @@ public final class ActivityManagerService extends ActivityManagerNative } if (mBadProcesses.getMap().size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Bad processes:"); + boolean printed = false; for (Map.Entry<String, SparseArray<Long>> procs : mBadProcesses.getMap().entrySet()) { + String pname = procs.getKey(); SparseArray<Long> uids = procs.getValue(); final int N = uids.size(); for (int i=0; i<N; i++) { - pw.print(" Bad process "); pw.print(procs.getKey()); - pw.print(" uid "); pw.print(uids.keyAt(i)); + int puid = uids.keyAt(i); + ProcessRecord r = mProcessNames.get(pname, puid); + if (dumpPackage != null && (r == null + || !dumpPackage.equals(r.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Bad processes:"); + } + pw.print(" Bad process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); pw.print(": crashed at time "); pw.println(uids.valueAt(i)); } @@ -8158,6 +8365,13 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(); pw.println(" mHomeProcess: " + mHomeProcess); + pw.println(" mPreviousProcess: " + mPreviousProcess); + if (dumpAll) { + StringBuilder sb = new StringBuilder(128); + sb.append(" mPreviousProcessVisibleTime: "); + TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb); + pw.println(sb); + } if (mHeavyWeightProcess != null) { pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess); } @@ -8165,11 +8379,18 @@ public final class ActivityManagerService extends ActivityManagerNative if (dumpAll) { pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange); if (mCompatModePackages.getPackages().size() > 0) { - pw.println(" mScreenCompatPackages:"); + boolean printed = false; for (Map.Entry<String, Integer> entry : mCompatModePackages.getPackages().entrySet()) { String pkg = entry.getKey(); int mode = entry.getValue(); + if (dumpPackage != null && !dumpPackage.equals(pkg)) { + continue; + } + if (!printed) { + pw.println(" mScreenCompatPackages:"); + printed = true; + } pw.print(" "); pw.print(pkg); pw.print(": "); pw.print(mode); pw.println(); } @@ -8210,20 +8431,29 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep); pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity); pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq); + pw.println(" mNumServiceProcs=" + mNumServiceProcs + + " mNewNumServiceProcs=" + mNewNumServiceProcs); } return true; } boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean needSep, boolean dumpAll) { + int opti, boolean needSep, boolean dumpAll, String dumpPackage) { if (mProcessesToGc.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are waiting to GC:"); + boolean printed = false; long now = SystemClock.uptimeMillis(); for (int i=0; i<mProcessesToGc.size(); i++) { ProcessRecord proc = mProcessesToGc.get(i); + if (dumpPackage != null && !dumpPackage.equals(proc.info.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Processes that are waiting to GC:"); + printed = true; + } pw.print(" Process "); pw.println(proc); pw.print(" lowMem="); pw.print(proc.reportLowMemory); pw.print(", last gced="); @@ -8246,29 +8476,32 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" OOM levels:"); pw.print(" SYSTEM_ADJ: "); pw.println(ProcessList.SYSTEM_ADJ); - pw.print(" CORE_SERVER_ADJ: "); pw.println(ProcessList.CORE_SERVER_ADJ); + pw.print(" PERSISTENT_PROC_ADJ: "); pw.println(ProcessList.PERSISTENT_PROC_ADJ); pw.print(" FOREGROUND_APP_ADJ: "); pw.println(ProcessList.FOREGROUND_APP_ADJ); pw.print(" VISIBLE_APP_ADJ: "); pw.println(ProcessList.VISIBLE_APP_ADJ); pw.print(" PERCEPTIBLE_APP_ADJ: "); pw.println(ProcessList.PERCEPTIBLE_APP_ADJ); pw.print(" HEAVY_WEIGHT_APP_ADJ: "); pw.println(ProcessList.HEAVY_WEIGHT_APP_ADJ); pw.print(" BACKUP_APP_ADJ: "); pw.println(ProcessList.BACKUP_APP_ADJ); - pw.print(" SECONDARY_SERVER_ADJ: "); pw.println(ProcessList.SECONDARY_SERVER_ADJ); + pw.print(" SERVICE_ADJ: "); pw.println(ProcessList.SERVICE_ADJ); pw.print(" HOME_APP_ADJ: "); pw.println(ProcessList.HOME_APP_ADJ); + pw.print(" PREVIOUS_APP_ADJ: "); pw.println(ProcessList.PREVIOUS_APP_ADJ); + pw.print(" SERVICE_B_ADJ: "); pw.println(ProcessList.SERVICE_B_ADJ); pw.print(" HIDDEN_APP_MIN_ADJ: "); pw.println(ProcessList.HIDDEN_APP_MIN_ADJ); - pw.print(" EMPTY_APP_ADJ: "); pw.println(ProcessList.EMPTY_APP_ADJ); + pw.print(" HIDDEN_APP_MAX_ADJ: "); pw.println(ProcessList.HIDDEN_APP_MAX_ADJ); if (needSep) pw.println(" "); needSep = true; pw.println(" Process OOM control:"); dumpProcessOomList(pw, this, mLruProcesses, " ", - "Proc", "PERS", true); + "Proc", "PERS", true, null); needSep = true; } - needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll); + needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, null); pw.println(); pw.println(" mHomeProcess: " + mHomeProcess); + pw.println(" mPreviousProcess: " + mPreviousProcess); if (mHeavyWeightProcess != null) { pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess); } @@ -8549,8 +8782,8 @@ public final class ActivityManagerService extends ActivityManagerNative try { TransferPipe tp = new TransferPipe(); try { - r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r, - innerPrefix, args); + r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), + r.appToken, innerPrefix, args); tp.go(fd); } finally { tp.kill(); @@ -8564,64 +8797,103 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll) { + int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)"); if (dumpAll) { if (mRegisteredReceivers.size() > 0) { - pw.println(" Registered Receivers:"); + boolean printed = false; Iterator it = mRegisteredReceivers.values().iterator(); while (it.hasNext()) { ReceiverList r = (ReceiverList)it.next(); + if (dumpPackage != null && (r.app == null || + !dumpPackage.equals(r.app.info.packageName))) { + continue; + } + if (!printed) { + pw.println(" Registered Receivers:"); + needSep = true; + printed = true; + } pw.print(" * "); pw.println(r); r.dump(pw, " "); } } - - pw.println(); - pw.println(" Receiver Resolver Table:"); - mReceiverResolver.dump(pw, null, " ", null, false); - needSep = true; + + if (mReceiverResolver.dump(pw, needSep ? + "\n Receiver Resolver Table:" : " Receiver Resolver Table:", + " ", dumpPackage, false)) { + needSep = true; + } } if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0 || mPendingBroadcast != null) { - if (mParallelBroadcasts.size() > 0) { - pw.println(); - pw.println(" Active broadcasts:"); - } + boolean printed = false; for (int i=mParallelBroadcasts.size()-1; i>=0; i--) { + BroadcastRecord br = mParallelBroadcasts.get(i); + if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Active broadcasts:"); + } pw.println(" Broadcast #" + i + ":"); - mParallelBroadcasts.get(i).dump(pw, " "); - } - if (mOrderedBroadcasts.size() > 0) { - pw.println(); - pw.println(" Active ordered broadcasts:"); + br.dump(pw, " "); } + printed = false; for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) { - pw.println(" Serialized Broadcast #" + i + ":"); + BroadcastRecord br = mOrderedBroadcasts.get(i); + if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Active ordered broadcasts:"); + } + pw.println(" Ordered Broadcast #" + i + ":"); mOrderedBroadcasts.get(i).dump(pw, " "); } - pw.println(); - pw.println(" Pending broadcast:"); - if (mPendingBroadcast != null) { - mPendingBroadcast.dump(pw, " "); - } else { - pw.println(" (null)"); + if (dumpPackage == null || (mPendingBroadcast != null + && dumpPackage.equals(mPendingBroadcast.callerPackage))) { + if (needSep) { + pw.println(); + } + pw.println(" Pending broadcast:"); + if (mPendingBroadcast != null) { + mPendingBroadcast.dump(pw, " "); + } else { + pw.println(" (null)"); + } + needSep = true; } - needSep = true; } - if (needSep) { - pw.println(); - } - pw.println(" Historical broadcasts:"); + boolean printed = false; for (int i=0; i<MAX_BROADCAST_HISTORY; i++) { BroadcastRecord r = mBroadcastHistory[i]; if (r == null) { break; } + if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Historical broadcasts:"); + printed = true; + } if (dumpAll) { pw.print(" Historical Broadcast #"); pw.print(i); pw.println(":"); r.dump(pw, " "); @@ -8635,8 +8907,11 @@ public final class ActivityManagerService extends ActivityManagerNative } needSep = true; - if (mStickyBroadcasts != null) { - pw.println(); + if (mStickyBroadcasts != null && dumpPackage == null) { + if (needSep) { + pw.println(); + } + needSep = true; pw.println(" Sticky broadcasts:"); StringBuilder sb = new StringBuilder(128); for (Map.Entry<String, ArrayList<Intent>> ent @@ -8676,7 +8951,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, boolean dumpClient) { + int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { boolean needSep = false; ItemMatcher matcher = new ItemMatcher(); @@ -8684,7 +8959,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)"); if (mServices.size() > 0) { - pw.println(" Active services:"); + boolean printed = false; long nowReal = SystemClock.elapsedRealtime(); Iterator<ServiceRecord> it = mServices.values().iterator(); needSep = false; @@ -8693,6 +8968,13 @@ public final class ActivityManagerService extends ActivityManagerNative if (!matcher.match(r, r.name)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + pw.println(" Active services:"); + printed = true; + } if (needSep) { pw.println(); } @@ -8706,6 +8988,20 @@ public final class ActivityManagerService extends ActivityManagerNative TimeUtils.formatDuration(r.createTime, nowReal, pw); pw.print(" started="); pw.print(r.startRequested); pw.print(" connections="); pw.println(r.connections.size()); + if (r.connections.size() > 0) { + pw.println(" Connections:"); + for (ArrayList<ConnectionRecord> clist : r.connections.values()) { + for (int i=0; i<clist.size(); i++) { + ConnectionRecord conn = clist.get(i); + pw.print(" "); + pw.print(conn.binding.intent.intent.getIntent().toShortString( + false, false, false)); + pw.print(" -> "); + ProcessRecord proc = conn.binding.client; + pw.println(proc != null ? proc.toShortString() : "null"); + } + } + } } if (dumpClient && r.app != null && r.app.thread != null) { pw.println(" Client:"); @@ -8730,17 +9026,25 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; } } - needSep = true; + needSep = printed; } if (mPendingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Pending services:"); + boolean printed = false; for (int i=0; i<mPendingServices.size(); i++) { ServiceRecord r = mPendingServices.get(i); if (!matcher.match(r, r.name)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Pending services:"); + printed = true; + } pw.print(" * Pending "); pw.println(r); r.dump(pw, " "); } @@ -8748,13 +9052,21 @@ public final class ActivityManagerService extends ActivityManagerNative } if (mRestartingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Restarting services:"); + boolean printed = false; for (int i=0; i<mRestartingServices.size(); i++) { ServiceRecord r = mRestartingServices.get(i); if (!matcher.match(r, r.name)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Restarting services:"); + printed = true; + } pw.print(" * Restarting "); pw.println(r); r.dump(pw, " "); } @@ -8762,13 +9074,21 @@ public final class ActivityManagerService extends ActivityManagerNative } if (mStoppingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Stopping services:"); + boolean printed = false; for (int i=0; i<mStoppingServices.size(); i++) { ServiceRecord r = mStoppingServices.get(i); if (!matcher.match(r, r.name)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Stopping services:"); + printed = true; + } pw.print(" * Stopping "); pw.println(r); r.dump(pw, " "); } @@ -8777,8 +9097,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (dumpAll) { if (mServiceConnections.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Connection bindings to services:"); + boolean printed = false; Iterator<ArrayList<ConnectionRecord>> it = mServiceConnections.values().iterator(); while (it.hasNext()) { @@ -8788,6 +9107,16 @@ public final class ActivityManagerService extends ActivityManagerNative if (!matcher.match(cr.binding.service, cr.binding.service.name)) { continue; } + if (dumpPackage != null && (cr.binding.client == null + || !dumpPackage.equals(cr.binding.client.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Connection bindings to services:"); + printed = true; + } pw.print(" * "); pw.println(cr); cr.dump(pw, " "); } @@ -8800,7 +9129,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll) { + int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; ItemMatcher matcher = new ItemMatcher(); @@ -8808,8 +9137,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)"); if (mProvidersByClass.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Published content providers (by class):"); + boolean printed = false; Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = mProvidersByClass.entrySet().iterator(); while (it.hasNext()) { @@ -8824,28 +9152,38 @@ public final class ActivityManagerService extends ActivityManagerNative if (!matcher.match(r, comp)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(comp.getPackageName())) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Published content providers (by class):"); + printed = true; + } pw.print(" * "); pw.print(cls); pw.print(" ("); - pw.print(comp.flattenToShortString()); pw.print(")"); + pw.print(comp.flattenToShortString()); pw.println(")"); if (dumpAll) { - pw.println(); r.dump(pw, " "); } else { - pw.print(" * "); pw.print(e.getKey().flattenToShortString()); if (r.proc != null) { - pw.println(":"); pw.print(" "); pw.println(r.proc); } else { pw.println(); } + if (r.clients.size() > 0) { + pw.println(" Clients:"); + for (ProcessRecord cproc : r.clients) { + pw.print(" - "); pw.println(cproc); + } + } } } - needSep = true; } if (dumpAll) { if (mProvidersByName.size() > 0) { - pw.println(" "); - pw.println(" Authority to provider mappings:"); + boolean printed = false; Iterator<Map.Entry<String, ContentProviderRecord>> it = mProvidersByName.entrySet().iterator(); while (it.hasNext()) { @@ -8854,25 +9192,42 @@ public final class ActivityManagerService extends ActivityManagerNative if (!matcher.match(r, r.name)) { continue; } - pw.print(" "); pw.print(e.getKey()); pw.print(": "); - pw.println(r); + if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Authority to provider mappings:"); + printed = true; + } + pw.print(" "); pw.print(e.getKey()); pw.println(":"); + pw.print(" "); pw.println(r); } - needSep = true; } } if (mLaunchingProviders.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Launching content providers:"); + boolean printed = false; for (int i=mLaunchingProviders.size()-1; i>=0; i--) { + ContentProviderRecord r = mLaunchingProviders.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Launching content providers:"); + printed = true; + } pw.print(" Launching #"); pw.print(i); pw.print(": "); - pw.println(mLaunchingProviders.get(i)); + pw.println(r); } - needSep = true; } if (mGrantedUriPermissions.size() > 0) { - pw.println(); + if (needSep) pw.println(); + needSep = true; pw.println("Granted Uri Permissions:"); for (int i=0; i<mGrantedUriPermissions.size(); i++) { int uid = mGrantedUriPermissions.keyAt(i); @@ -8894,16 +9249,24 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll) { + int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; - if (this.mIntentSenderRecords.size() > 0) { - pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)"); + if (mIntentSenderRecords.size() > 0) { + boolean printed = false; Iterator<WeakReference<PendingIntentRecord>> it = mIntentSenderRecords.values().iterator(); while (it.hasNext()) { WeakReference<PendingIntentRecord> ref = it.next(); PendingIntentRecord rec = ref != null ? ref.get(): null; + if (dumpPackage != null && (rec == null + || !dumpPackage.equals(rec.key.packageName))) { + continue; + } + if (!printed) { + pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)"); + printed = true; + } needSep = true; if (rec != null) { pw.print(" * "); pw.println(rec); @@ -8920,13 +9283,17 @@ public final class ActivityManagerService extends ActivityManagerNative } private static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List list, - String prefix, String label, boolean complete, boolean brief, boolean client) { + String prefix, String label, boolean complete, boolean brief, boolean client, + String dumpPackage) { TaskRecord lastTask = null; boolean needNL = false; final String innerPrefix = prefix + " "; final String[] args = new String[0]; for (int i=list.size()-1; i>=0; i--) { final ActivityRecord r = (ActivityRecord)list.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.packageName)) { + continue; + } final boolean full = !brief && (complete || !r.isInHistory()); if (needNL) { pw.println(" "); @@ -8966,8 +9333,8 @@ public final class ActivityManagerService extends ActivityManagerNative try { TransferPipe tp = new TransferPipe(); try { - r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r, - innerPrefix, args); + r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), + r.appToken, innerPrefix, args); // Short timeout, since blocking here can // deadlock with the application. tp.go(fd, 2000); @@ -8994,11 +9361,15 @@ public final class ActivityManagerService extends ActivityManagerNative private static final int dumpProcessList(PrintWriter pw, ActivityManagerService service, List list, - String prefix, String normalLabel, String persistentLabel) { + String prefix, String normalLabel, String persistentLabel, + String dumpPackage) { int numPers = 0; final int N = list.size()-1; for (int i=N; i>=0; i--) { ProcessRecord r = (ProcessRecord)list.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } pw.println(String.format("%s%s #%2d: %s", prefix, (r.persistent ? persistentLabel : normalLabel), i, r.toString())); @@ -9009,17 +9380,25 @@ public final class ActivityManagerService extends ActivityManagerNative return numPers; } - private static final void dumpProcessOomList(PrintWriter pw, + private static final boolean dumpProcessOomList(PrintWriter pw, ActivityManagerService service, List<ProcessRecord> origList, String prefix, String normalLabel, String persistentLabel, - boolean inclDetails) { + boolean inclDetails, String dumpPackage) { ArrayList<Pair<ProcessRecord, Integer>> list = new ArrayList<Pair<ProcessRecord, Integer>>(origList.size()); for (int i=0; i<origList.size(); i++) { + ProcessRecord r = origList.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i)); } + if (list.size() <= 0) { + return false; + } + Comparator<Pair<ProcessRecord, Integer>> comparator = new Comparator<Pair<ProcessRecord, Integer>>() { @Override @@ -9042,20 +9421,21 @@ public final class ActivityManagerService extends ActivityManagerNative final long curUptime = SystemClock.uptimeMillis(); final long uptimeSince = curUptime - service.mLastPowerCheckUptime; - final int N = list.size()-1; - for (int i=N; i>=0; i--) { + for (int i=list.size()-1; i>=0; i--) { ProcessRecord r = list.get(i).first; String oomAdj; - if (r.setAdj >= ProcessList.EMPTY_APP_ADJ) { - oomAdj = buildOomTag("empty", null, r.setAdj, ProcessList.EMPTY_APP_ADJ); - } else if (r.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { + if (r.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { oomAdj = buildOomTag("bak", " ", r.setAdj, ProcessList.HIDDEN_APP_MIN_ADJ); + } else if (r.setAdj >= ProcessList.SERVICE_B_ADJ) { + oomAdj = buildOomTag("svcb ", null, r.setAdj, ProcessList.SERVICE_B_ADJ); + } else if (r.setAdj >= ProcessList.PREVIOUS_APP_ADJ) { + oomAdj = buildOomTag("prev ", null, r.setAdj, ProcessList.PREVIOUS_APP_ADJ); } else if (r.setAdj >= ProcessList.HOME_APP_ADJ) { oomAdj = buildOomTag("home ", null, r.setAdj, ProcessList.HOME_APP_ADJ); - } else if (r.setAdj >= ProcessList.SECONDARY_SERVER_ADJ) { - oomAdj = buildOomTag("svc", " ", r.setAdj, ProcessList.SECONDARY_SERVER_ADJ); + } else if (r.setAdj >= ProcessList.SERVICE_ADJ) { + oomAdj = buildOomTag("svc ", null, r.setAdj, ProcessList.SERVICE_ADJ); } else if (r.setAdj >= ProcessList.BACKUP_APP_ADJ) { - oomAdj = buildOomTag("bckup", null, r.setAdj, ProcessList.BACKUP_APP_ADJ); + oomAdj = buildOomTag("bkup ", null, r.setAdj, ProcessList.BACKUP_APP_ADJ); } else if (r.setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { oomAdj = buildOomTag("hvy ", null, r.setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ); } else if (r.setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -9064,8 +9444,8 @@ public final class ActivityManagerService extends ActivityManagerNative oomAdj = buildOomTag("vis ", null, r.setAdj, ProcessList.VISIBLE_APP_ADJ); } else if (r.setAdj >= ProcessList.FOREGROUND_APP_ADJ) { oomAdj = buildOomTag("fore ", null, r.setAdj, ProcessList.FOREGROUND_APP_ADJ); - } else if (r.setAdj >= ProcessList.CORE_SERVER_ADJ) { - oomAdj = buildOomTag("core ", null, r.setAdj, ProcessList.CORE_SERVER_ADJ); + } else if (r.setAdj >= ProcessList.PERSISTENT_PROC_ADJ) { + oomAdj = buildOomTag("pers ", null, r.setAdj, ProcessList.PERSISTENT_PROC_ADJ); } else if (r.setAdj >= ProcessList.SYSTEM_ADJ) { oomAdj = buildOomTag("sys ", null, r.setAdj, ProcessList.SYSTEM_ADJ); } else { @@ -9093,8 +9473,8 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(String.format("%s%s #%2d: adj=%s/%s%s trm=%2d %s (%s)", prefix, (r.persistent ? persistentLabel : normalLabel), - N-list.get(i).second, oomAdj, schedGroup, foreground, r.trimMemoryLevel, - r.toShortString(), r.adjType)); + (origList.size()-1)-list.get(i).second, oomAdj, schedGroup, + foreground, r.trimMemoryLevel, r.toShortString(), r.adjType)); if (r.adjSource != null || r.adjTarget != null) { pw.print(prefix); pw.print(" "); @@ -9166,6 +9546,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + return true; } ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, String[] args) { @@ -9237,16 +9618,20 @@ public final class ActivityManagerService extends ActivityManagerNative final static class MemItem { final String label; + final String shortLabel; final long pss; + final int id; ArrayList<MemItem> subitems; - public MemItem(String _label, long _pss) { + public MemItem(String _label, String _shortLabel, long _pss, int _id) { label = _label; + shortLabel = _shortLabel; pss = _pss; + id = _id; } } - final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items, + static final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items, boolean sort) { if (sort) { Collections.sort(items, new Comparator<MemItem>() { @@ -9264,16 +9649,59 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<items.size(); i++) { MemItem mi = items.get(i); - pw.print(prefix); pw.printf("%7d Kb: ", mi.pss); pw.println(mi.label); + pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label); if (mi.subitems != null) { dumpMemItems(pw, prefix + " ", mi.subitems, true); } } } + // These are in KB. + static final long[] DUMP_MEM_BUCKETS = new long[] { + 5*1024, 7*1024, 10*1024, 15*1024, 20*1024, 30*1024, 40*1024, 80*1024, + 120*1024, 160*1024, 200*1024, + 250*1024, 300*1024, 350*1024, 400*1024, 500*1024, 600*1024, 800*1024, + 1*1024*1024, 2*1024*1024, 5*1024*1024, 10*1024*1024, 20*1024*1024 + }; + + static final void appendMemBucket(StringBuilder out, long memKB, String label, + boolean stackLike) { + int start = label.lastIndexOf('.'); + if (start >= 0) start++; + else start = 0; + int end = label.length(); + for (int i=0; i<DUMP_MEM_BUCKETS.length; i++) { + if (DUMP_MEM_BUCKETS[i] >= memKB) { + long bucket = DUMP_MEM_BUCKETS[i]/1024; + out.append(bucket); + out.append(stackLike ? "MB." : "MB "); + out.append(label, start, end); + return; + } + } + out.append(memKB/1024); + out.append(stackLike ? "MB." : "MB "); + out.append(label, start, end); + } + + static final int[] DUMP_MEM_OOM_ADJ = new int[] { + ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ, + ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, + ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, + ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.HIDDEN_APP_MAX_ADJ + }; + static final String[] DUMP_MEM_OOM_LABEL = new String[] { + "System", "Persistent", "Foreground", + "Visible", "Perceptible", "Heavy Weight", + "Backup", "A Services", "Home", "Previous", + "B Services", "Background" + }; + final void dumpApplicationMemoryUsage(FileDescriptor fd, - PrintWriter pw, String prefix, String[] args, boolean brief) { + PrintWriter pw, String prefix, String[] args, boolean brief, + PrintWriter categoryPw, StringBuilder outTag, StringBuilder outStack) { boolean dumpAll = false; + boolean oomOnly = false; int opti = 0; while (opti < args.length) { @@ -9284,9 +9712,12 @@ public final class ActivityManagerService extends ActivityManagerNative opti++; if ("-a".equals(opt)) { dumpAll = true; + } else if ("--oom".equals(opt)) { + oomOnly = true; } else if ("-h".equals(opt)) { - pw.println("meminfo dump options: [-a] [process]"); + pw.println("meminfo dump options: [-a] [--oom] [process]"); pw.println(" -a: include all available information for each process."); + pw.println(" --oom: only show processes organized by oom adj."); pw.println("If [process] is specified it can be the name or "); pw.println("pid of a specific process to dump."); return; @@ -9324,18 +9755,9 @@ public final class ActivityManagerService extends ActivityManagerNative long nativePss=0, dalvikPss=0, otherPss=0; long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; - final int[] oomAdj = new int[] { - ProcessList.SYSTEM_ADJ, ProcessList.CORE_SERVER_ADJ, ProcessList.FOREGROUND_APP_ADJ, - ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, - ProcessList.BACKUP_APP_ADJ, ProcessList.SECONDARY_SERVER_ADJ, ProcessList.HOME_APP_ADJ, ProcessList.EMPTY_APP_ADJ - }; - final String[] oomLabel = new String[] { - "System", "Persistent", "Foreground", - "Visible", "Perceptible", "Heavy Weight", - "Backup", "Services", "Home", "Background" - }; - long oomPss[] = new long[oomLabel.length]; - ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])new ArrayList[oomLabel.length]; + long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length]; + ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[]) + new ArrayList[DUMP_MEM_OOM_LABEL.length]; long totalPss = 0; @@ -9365,7 +9787,7 @@ public final class ActivityManagerService extends ActivityManagerNative long myTotalPss = mi.getTotalPss(); totalPss += myTotalPss; MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")", - myTotalPss); + r.processName, myTotalPss, 0); procMems.add(pssItem); nativePss += mi.nativePss; @@ -9378,7 +9800,8 @@ public final class ActivityManagerService extends ActivityManagerNative } for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) { - if (r.setAdj <= oomAdj[oomIndex] || oomIndex == (oomPss.length-1)) { + if (r.setAdj <= DUMP_MEM_OOM_ADJ[oomIndex] + || oomIndex == (oomPss.length-1)) { oomPss[oomIndex] += myTotalPss; if (oomProcs[oomIndex] == null) { oomProcs[oomIndex] = new ArrayList<MemItem>(); @@ -9394,23 +9817,88 @@ public final class ActivityManagerService extends ActivityManagerNative if (!isCheckinRequest && procs.size() > 1) { ArrayList<MemItem> catMems = new ArrayList<MemItem>(); - catMems.add(new MemItem("Native", nativePss)); - catMems.add(new MemItem("Dalvik", dalvikPss)); - catMems.add(new MemItem("Unknown", otherPss)); + catMems.add(new MemItem("Native", "Native", nativePss, -1)); + catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, -2)); + catMems.add(new MemItem("Unknown", "Unknown", otherPss, -3)); for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { - catMems.add(new MemItem(Debug.MemoryInfo.getOtherLabel(j), miscPss[j])); + String label = Debug.MemoryInfo.getOtherLabel(j); + catMems.add(new MemItem(label, label, miscPss[j], j)); } ArrayList<MemItem> oomMems = new ArrayList<MemItem>(); for (int j=0; j<oomPss.length; j++) { if (oomPss[j] != 0) { - MemItem item = new MemItem(oomLabel[j], oomPss[j]); + String label = DUMP_MEM_OOM_LABEL[j]; + MemItem item = new MemItem(label, label, oomPss[j], + DUMP_MEM_OOM_ADJ[j]); item.subitems = oomProcs[j]; oomMems.add(item); } } - if (!brief) { + if (outTag != null || outStack != null) { + if (outTag != null) { + appendMemBucket(outTag, totalPss, "total", false); + } + if (outStack != null) { + appendMemBucket(outStack, totalPss, "total", true); + } + boolean firstLine = true; + for (int i=0; i<oomMems.size(); i++) { + MemItem miCat = oomMems.get(i); + if (miCat.subitems == null || miCat.subitems.size() < 1) { + continue; + } + if (miCat.id < ProcessList.SERVICE_ADJ + || miCat.id == ProcessList.HOME_APP_ADJ + || miCat.id == ProcessList.PREVIOUS_APP_ADJ) { + if (outTag != null && miCat.id <= ProcessList.FOREGROUND_APP_ADJ) { + outTag.append(" / "); + } + if (outStack != null) { + if (miCat.id >= ProcessList.FOREGROUND_APP_ADJ) { + if (firstLine) { + outStack.append(":"); + firstLine = false; + } + outStack.append("\n\t at "); + } else { + outStack.append("$"); + } + } + for (int j=0; j<miCat.subitems.size(); j++) { + MemItem mi = miCat.subitems.get(j); + if (j > 0) { + if (outTag != null) { + outTag.append(" "); + } + if (outStack != null) { + outStack.append("$"); + } + } + if (outTag != null && miCat.id <= ProcessList.FOREGROUND_APP_ADJ) { + appendMemBucket(outTag, mi.pss, mi.shortLabel, false); + } + if (outStack != null) { + appendMemBucket(outStack, mi.pss, mi.shortLabel, true); + } + } + if (outStack != null && miCat.id >= ProcessList.FOREGROUND_APP_ADJ) { + outStack.append("("); + for (int k=0; k<DUMP_MEM_OOM_ADJ.length; k++) { + if (DUMP_MEM_OOM_ADJ[k] == miCat.id) { + outStack.append(DUMP_MEM_OOM_LABEL[k]); + outStack.append(":"); + outStack.append(DUMP_MEM_OOM_ADJ[k]); + } + } + outStack.append(")"); + } + } + } + } + + if (!brief && !oomOnly) { pw.println(); pw.println("Total PSS by process:"); dumpMemItems(pw, " ", procMems, true); @@ -9418,13 +9906,14 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println("Total PSS by OOM adjustment:"); dumpMemItems(pw, " ", oomMems, false); - if (!brief) { - pw.println(); - pw.println("Total PSS by category:"); - dumpMemItems(pw, " ", catMems, true); + if (!oomOnly) { + PrintWriter out = categoryPw != null ? categoryPw : pw; + out.println(); + out.println("Total PSS by category:"); + dumpMemItems(out, " ", catMems, true); } pw.println(); - pw.print("Total PSS: "); pw.print(totalPss); pw.println(" Kb"); + pw.print("Total PSS: "); pw.print(totalPss); pw.println(" kB"); } } @@ -9758,7 +10247,10 @@ public final class ActivityManagerService extends ActivityManagerNative if (app == mHomeProcess) { mHomeProcess = null; } - + if (app == mPreviousProcess) { + mPreviousProcess = null; + } + if (restart) { // We have components that still need to be running in the // process, so re-launch it. @@ -12838,7 +13330,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { final long origId = Binder.clearCallingIdentity(); - updateConfigurationLocked(values, null, true); + updateConfigurationLocked(values, null, true, false); Binder.restoreCallingIdentity(origId); } } @@ -12861,7 +13353,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (values != null) { Settings.System.clearConfiguration(values); } - updateConfigurationLocked(values, null, false); + updateConfigurationLocked(values, null, false, false); Binder.restoreCallingIdentity(origId); } } @@ -12875,7 +13367,7 @@ public final class ActivityManagerService extends ActivityManagerNative * @param persistent TODO */ public boolean updateConfigurationLocked(Configuration values, - ActivityRecord starting, boolean persistent) { + ActivityRecord starting, boolean persistent, boolean initLocale) { int changes = 0; boolean kept = true; @@ -12890,7 +13382,7 @@ public final class ActivityManagerService extends ActivityManagerNative EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); - if (values.locale != null) { + if (values.locale != null && !initLocale) { saveLocaleLocked(values.locale, !values.locale.equals(mConfiguration.locale), values.userSetLocale); @@ -12903,10 +13395,12 @@ public final class ActivityManagerService extends ActivityManagerNative newConfig.seq = mConfigurationSeq; mConfiguration = newConfig; Slog.i(TAG, "Config changed: " + newConfig); - + + final Configuration configCopy = new Configuration(mConfiguration); + AttributeCache ac = AttributeCache.instance(); if (ac != null) { - ac.updateConfiguration(mConfiguration); + ac.updateConfiguration(configCopy); } // Make sure all resources in our process are updated @@ -12916,11 +13410,11 @@ public final class ActivityManagerService extends ActivityManagerNative // boot, where the first config change needs to guarantee // all resources have that config before following boot // code is executed. - mSystemThread.applyConfigurationToResources(newConfig); + mSystemThread.applyConfigurationToResources(configCopy); if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) { Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); - msg.obj = new Configuration(mConfiguration); + msg.obj = new Configuration(configCopy); mHandler.sendMessage(msg); } @@ -12930,7 +13424,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.thread != null) { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + app.processName + " new config " + mConfiguration); - app.thread.scheduleConfigurationChanged(mConfiguration); + app.thread.scheduleConfigurationChanged(configCopy); } } catch (Exception e) { } @@ -12991,7 +13485,7 @@ public final class ActivityManagerService extends ActivityManagerNative // ========================================================= private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj, - ProcessRecord TOP_APP, boolean recursed) { + ProcessRecord TOP_APP, boolean recursed, boolean doingAll) { if (mAdjSeq == app.adjSeq) { // This adjustment has already been computed. If we are calling // from the top, we may have already computed our adjustment with @@ -13006,7 +13500,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.thread == null) { app.adjSeq = mAdjSeq; app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; - return (app.curAdj=ProcessList.EMPTY_APP_ADJ); + return (app.curAdj=ProcessList.HIDDEN_APP_MAX_ADJ); } app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; @@ -13117,6 +13611,7 @@ public final class ActivityManagerService extends ActivityManagerNative adj = ProcessList.PERCEPTIBLE_APP_ADJ; app.adjType = "stopping"; } + app.hidden = false; app.foregroundActivities = true; } } @@ -13156,6 +13651,17 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "home"; } + if (adj > ProcessList.PREVIOUS_APP_ADJ && app == mPreviousProcess + && app.activities.size() > 0) { + // This was the previous process that showed UI to the user. + // We want to try to keep it around more aggressively, to give + // a good experience around switching between two apps. + adj = ProcessList.PREVIOUS_APP_ADJ; + schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; + app.hidden = false; + app.adjType = "previous"; + } + if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj + " reason=" + app.adjType); @@ -13190,7 +13696,7 @@ public final class ActivityManagerService extends ActivityManagerNative // go to the LRU list because it may be pretty heavy with // UI stuff. We'll tag it with a label just to help // debug and understand what is going on. - if (adj > ProcessList.SECONDARY_SERVER_ADJ) { + if (adj > ProcessList.SERVICE_ADJ) { app.adjType = "started-bg-ui-services"; } } else { @@ -13198,8 +13704,8 @@ public final class ActivityManagerService extends ActivityManagerNative // This service has seen some activity within // recent memory, so we will keep its process ahead // of the background processes. - if (adj > ProcessList.SECONDARY_SERVER_ADJ) { - adj = ProcessList.SECONDARY_SERVER_ADJ; + if (adj > ProcessList.SERVICE_ADJ) { + adj = ProcessList.SERVICE_ADJ; app.adjType = "started-services"; app.hidden = false; } @@ -13207,7 +13713,7 @@ public final class ActivityManagerService extends ActivityManagerNative // If we have let the service slide into the background // state, still have some text describing what it is doing // even though the service no longer has an impact. - if (adj > ProcessList.SECONDARY_SERVER_ADJ) { + if (adj > ProcessList.SERVICE_ADJ) { app.adjType = "started-bg-services"; } } @@ -13241,7 +13747,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP, true); + client, myHiddenAdj, TOP_APP, true, doingAll); String adjType = null; if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { // Not doing bind OOM management, so treat @@ -13285,10 +13791,17 @@ public final class ActivityManagerService extends ActivityManagerNative if ((cr.flags&(Context.BIND_ABOVE_CLIENT |Context.BIND_IMPORTANT)) != 0) { adj = clientAdj; - } else if (clientAdj >= ProcessList.VISIBLE_APP_ADJ) { + } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 + && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ + && adj > ProcessList.PERCEPTIBLE_APP_ADJ) { + adj = ProcessList.PERCEPTIBLE_APP_ADJ; + } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) { adj = clientAdj; } else { - adj = ProcessList.VISIBLE_APP_ADJ; + app.pendingUiClean = true; + if (adj > ProcessList.VISIBLE_APP_ADJ) { + adj = ProcessList.VISIBLE_APP_ADJ; + } } if (!client.hidden) { app.hidden = false; @@ -13371,7 +13884,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } int clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP, true); + client, myHiddenAdj, TOP_APP, true, doingAll); if (adj > clientAdj) { if (app.hasShownUi && app != mHomeProcess && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -13442,11 +13955,23 @@ public final class ActivityManagerService extends ActivityManagerNative adj = ProcessList.PERCEPTIBLE_APP_ADJ; } else if (adj < ProcessList.HIDDEN_APP_MIN_ADJ) { adj = ProcessList.HIDDEN_APP_MIN_ADJ; - } else if (adj < ProcessList.EMPTY_APP_ADJ) { + } else if (adj < ProcessList.HIDDEN_APP_MAX_ADJ) { adj++; } } + if (adj == ProcessList.SERVICE_ADJ) { + if (doingAll) { + app.serviceb = mNewNumServiceProcs > (mNumServiceProcs/3); + mNewNumServiceProcs++; + } + if (app.serviceb) { + adj = ProcessList.SERVICE_B_ADJ; + } + } else { + app.serviceb = false; + } + app.curAdj = adj; app.curSchedGroup = schedGroup; @@ -13687,7 +14212,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean updateOomAdjLocked( - ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) { + ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP, boolean doingAll) { app.hiddenAdj = hiddenAdj; if (app.thread == null) { @@ -13698,7 +14223,7 @@ public final class ActivityManagerService extends ActivityManagerNative boolean success = true; - computeOomAdjLocked(app, hiddenAdj, TOP_APP, false); + computeOomAdjLocked(app, hiddenAdj, TOP_APP, false, doingAll); if (app.curRawAdj != app.setRawAdj) { if (false) { @@ -13732,11 +14257,12 @@ public final class ActivityManagerService extends ActivityManagerNative app.setRawAdj = app.curRawAdj; } + if (app.curAdj != app.setAdj) { if (Process.setOomAdj(app.pid, app.curAdj)) { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( - TAG, "Set app " + app.processName + - " oom adj to " + app.curAdj + " because " + app.adjType); + TAG, "Set " + app.pid + " " + app.processName + + " adj " + app.curAdj + ": " + app.adjType); app.setAdj = app.curAdj; } else { success = false; @@ -13800,7 +14326,7 @@ public final class ActivityManagerService extends ActivityManagerNative mAdjSeq++; - boolean success = updateOomAdjLocked(app, app.hiddenAdj, TOP_APP); + boolean success = updateOomAdjLocked(app, app.hiddenAdj, TOP_APP, false); final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ && app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ; if (nowHidden != wasHidden) { @@ -13822,6 +14348,7 @@ public final class ActivityManagerService extends ActivityManagerNative } mAdjSeq++; + mNewNumServiceProcs = 0; // Let's determine how many processes we have running vs. // how many slots we have for background processes; we may want @@ -13837,13 +14364,12 @@ public final class ActivityManagerService extends ActivityManagerNative // application processes based on their current state. int i = mLruProcesses.size(); int curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ; - int numBg = 0; while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj); - updateOomAdjLocked(app, curHiddenAdj, TOP_APP); - if (curHiddenAdj < ProcessList.EMPTY_APP_ADJ + updateOomAdjLocked(app, curHiddenAdj, TOP_APP, true); + if (curHiddenAdj < ProcessList.HIDDEN_APP_MAX_ADJ && app.curAdj == curHiddenAdj) { step++; if (step >= factor) { @@ -13861,15 +14387,13 @@ public final class ActivityManagerService extends ActivityManagerNative app.processName, app.setAdj, "too many background"); app.killedBackground = true; Process.killProcessQuiet(app.pid); - } else { - numBg++; } - } else if (app.curAdj >= ProcessList.HOME_APP_ADJ) { - numBg++; } } } + mNumServiceProcs = mNewNumServiceProcs; + // Now determine the memory trimming level of background processes. // Unfortunately we need to start at the back of the list to do this // properly. We only do this if the number of background apps we @@ -13878,21 +14402,33 @@ public final class ActivityManagerService extends ActivityManagerNative // memory they want. if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/2)) { final int N = mLruProcesses.size(); - factor = numBg/3; + factor = numHidden/3; + int minFactor = 2; + if (mHomeProcess != null) minFactor++; + if (mPreviousProcess != null) minFactor++; + if (factor < minFactor) factor = minFactor; step = 0; int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; for (i=0; i<N; i++) { ProcessRecord app = mLruProcesses.get(i); - if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ && !app.killedBackground) { + if (app.curAdj >= ProcessList.HOME_APP_ADJ + && app.curAdj != ProcessList.SERVICE_B_ADJ + && !app.killedBackground) { if (app.trimMemoryLevel < curLevel && app.thread != null) { try { app.thread.scheduleTrimMemory(curLevel); } catch (RemoteException e) { } - if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { - // For these apps we will also finish their activities - // to help them free memory. - mMainStack.destroyActivitiesLocked(app, false); + if (false) { + // For now we won't do this; our memory trimming seems + // to be good enough at this point that destroying + // activities causes more harm than good. + if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE + && app != mHomeProcess && app != mPreviousProcess) { + // For these apps we will also finish their activities + // to help them free memory. + mMainStack.destroyActivitiesLocked(app, false, "trim"); + } } } app.trimMemoryLevel = curLevel; @@ -13956,7 +14492,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (mAlwaysFinishActivities) { - mMainStack.destroyActivitiesLocked(null, false); + mMainStack.destroyActivitiesLocked(null, false, "always-finish"); } } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index ce45bfb..c819114 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -29,6 +29,7 @@ import android.content.res.Configuration; import android.graphics.Bitmap; import android.os.Build; import android.os.Bundle; +import android.os.IBinder; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -48,9 +49,10 @@ import java.util.HashSet; /** * An entry in the history stack, representing an activity. */ -final class ActivityRecord extends IApplicationToken.Stub { +final class ActivityRecord { final ActivityManagerService service; // owner final ActivityStack stack; // owner + final IApplicationToken.Stub appToken; // window manager token final ActivityInfo info; // all about me final int launchedFromUid; // always the uid who started the activity. final Intent intent; // the original intent that generated us @@ -78,6 +80,7 @@ final class ActivityRecord extends IApplicationToken.Stub { ThumbnailHolder thumbHolder; // where our thumbnails should go. long launchTime; // when we starting launching this activity long startTime; // last time this activity was started + long lastVisibleTime; // last time this activity became visible long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity Configuration configuration; // configuration activity was last running in CompatibilityInfo compat;// last used compatibility mode @@ -186,6 +189,10 @@ final class ActivityRecord extends IApplicationToken.Stub { TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime="); TimeUtils.formatDuration(startTime, pw); pw.println(""); } + if (lastVisibleTime != 0) { + pw.print(prefix); pw.print("lastVisibleTime="); + TimeUtils.formatDuration(lastVisibleTime, pw); pw.println(""); + } if (waitingVisible || nowVisible) { pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible); pw.print(" nowVisible="); pw.println(nowVisible); @@ -200,6 +207,70 @@ final class ActivityRecord extends IApplicationToken.Stub { } } + static class Token extends IApplicationToken.Stub { + final WeakReference<ActivityRecord> weakActivity; + + Token(ActivityRecord activity) { + weakActivity = new WeakReference<ActivityRecord>(activity); + } + + @Override public void windowsDrawn() throws RemoteException { + ActivityRecord activity = weakActivity.get(); + if (activity != null) { + activity.windowsDrawn(); + } + } + + @Override public void windowsVisible() throws RemoteException { + ActivityRecord activity = weakActivity.get(); + if (activity != null) { + activity.windowsVisible(); + } + } + + @Override public void windowsGone() throws RemoteException { + ActivityRecord activity = weakActivity.get(); + if (activity != null) { + activity.windowsGone(); + } + } + + @Override public boolean keyDispatchingTimedOut() throws RemoteException { + ActivityRecord activity = weakActivity.get(); + if (activity != null) { + return activity.keyDispatchingTimedOut(); + } + return false; + } + + @Override public long getKeyDispatchingTimeout() throws RemoteException { + ActivityRecord activity = weakActivity.get(); + if (activity != null) { + return activity.getKeyDispatchingTimeout(); + } + return 0; + } + + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("Token{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(' '); + sb.append(weakActivity.get()); + sb.append('}'); + return sb.toString(); + } + } + + static ActivityRecord forToken(IBinder token) { + try { + return token != null ? ((Token)token).weakActivity.get() : null; + } catch (ClassCastException e) { + Slog.w(ActivityManagerService.TAG, "Bad activity token: " + token, e); + return null; + } + } + ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller, int _launchedFromUid, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, @@ -207,6 +278,7 @@ final class ActivityRecord extends IApplicationToken.Stub { boolean _componentSpecified) { service = _service; stack = _stack; + appToken = new Token(this); info = aInfo; launchedFromUid = _launchedFromUid; intent = _intent; @@ -445,7 +517,7 @@ final class ActivityRecord extends IApplicationToken.Stub { ar.add(intent); service.grantUriPermissionFromIntentLocked(callingUid, packageName, intent, getUriPermissionsLocked()); - app.thread.scheduleNewIntent(ar, this); + app.thread.scheduleNewIntent(ar, appToken); sent = true; } catch (RemoteException e) { Slog.w(ActivityManagerService.TAG, @@ -470,14 +542,14 @@ final class ActivityRecord extends IApplicationToken.Stub { void pauseKeyDispatchingLocked() { if (!keysPaused) { keysPaused = true; - service.mWindowManager.pauseKeyDispatching(this); + service.mWindowManager.pauseKeyDispatching(appToken); } } void resumeKeyDispatchingLocked() { if (keysPaused) { keysPaused = false; - service.mWindowManager.resumeKeyDispatching(this); + service.mWindowManager.resumeKeyDispatching(appToken); } } @@ -512,18 +584,18 @@ final class ActivityRecord extends IApplicationToken.Stub { public void startFreezingScreenLocked(ProcessRecord app, int configChanges) { if (mayFreezeScreenLocked(app)) { - service.mWindowManager.startAppFreezingScreen(this, configChanges); + service.mWindowManager.startAppFreezingScreen(appToken, configChanges); } } public void stopFreezingScreenLocked(boolean force) { if (force || frozenBeforeDestroy) { frozenBeforeDestroy = false; - service.mWindowManager.stopAppFreezingScreen(this, force); + service.mWindowManager.stopAppFreezingScreen(appToken, force); } } - public void windowsVisible() { + public void windowsDrawn() { synchronized(service) { if (launchTime != 0) { final long curTime = SystemClock.uptimeMillis(); @@ -555,11 +627,17 @@ final class ActivityRecord extends IApplicationToken.Stub { stack.mInitialStartTime = 0; } startTime = 0; + } + } + + public void windowsVisible() { + synchronized(service) { stack.reportActivityVisibleLocked(this); if (ActivityManagerService.DEBUG_SWITCH) Log.v( ActivityManagerService.TAG, "windowsVisible(): " + this); if (!nowVisible) { nowVisible = true; + lastVisibleTime = SystemClock.uptimeMillis(); if (!idle) { // Instead of doing the full stop routine here, let's just // hide any activities we now can, and let them stop when @@ -682,7 +760,7 @@ final class ActivityRecord extends IApplicationToken.Stub { } if (app != null && app.thread != null) { try { - app.thread.scheduleSleeping(this, _sleeping); + app.thread.scheduleSleeping(appToken, _sleeping); if (sleeping && !stack.mGoingToSleepActivities.contains(this)) { stack.mGoingToSleepActivities.add(this); } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 28c3bae..b5edc0a 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -145,7 +145,12 @@ final class ActivityStack { * running) activities. It contains HistoryRecord objects. */ final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>(); - + + /** + * Used for validating app tokens with window manager. + */ + final ArrayList<IBinder> mValidateAppTokens = new ArrayList<IBinder>(); + /** * List of running activities, sorted by recent usage. * The first entry in the list is the least recently used. @@ -294,11 +299,11 @@ final class ActivityStack { } } break; case PAUSE_TIMEOUT_MSG: { - IBinder token = (IBinder)msg.obj; + ActivityRecord r = (ActivityRecord)msg.obj; // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. - Slog.w(TAG, "Activity pause timeout for " + token); - activityPaused(token, true); + Slog.w(TAG, "Activity pause timeout for " + r); + activityPaused(r != null ? r.appToken : null, true); } break; case IDLE_TIMEOUT_MSG: { if (mService.mDidDexOpt) { @@ -310,20 +315,20 @@ final class ActivityStack { } // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. - IBinder token = (IBinder)msg.obj; - Slog.w(TAG, "Activity idle timeout for " + token); - activityIdleInternal(token, true, null); + ActivityRecord r = (ActivityRecord)msg.obj; + Slog.w(TAG, "Activity idle timeout for " + r); + activityIdleInternal(r != null ? r.appToken : null, true, null); } break; case DESTROY_TIMEOUT_MSG: { - IBinder token = (IBinder)msg.obj; + ActivityRecord r = (ActivityRecord)msg.obj; // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. - Slog.w(TAG, "Activity destroy timeout for " + token); - activityDestroyed(token); + Slog.w(TAG, "Activity destroy timeout for " + r); + activityDestroyed(r != null ? r.appToken : null); } break; case IDLE_NOW_MSG: { - IBinder token = (IBinder)msg.obj; - activityIdleInternal(token, false, null); + ActivityRecord r = (ActivityRecord)msg.obj; + activityIdleInternal(r != null ? r.appToken : null, false, null); } break; case LAUNCH_TIMEOUT_MSG: { if (mService.mDidDexOpt) { @@ -397,7 +402,7 @@ final class ActivityStack { while (i >= 0) { ActivityRecord r = mHistory.get(i); // Note: the taskId check depends on real taskId fields being non-zero - if (!r.finishing && (token != r) && (taskId != r.task.taskId)) { + if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId)) { return r; } i--; @@ -406,23 +411,17 @@ final class ActivityStack { } final int indexOfTokenLocked(IBinder token) { - try { - ActivityRecord r = (ActivityRecord)token; - return mHistory.indexOf(r); - } catch (ClassCastException e) { - Slog.w(TAG, "Bad activity token: " + token, e); - return -1; - } + return mHistory.indexOf(ActivityRecord.forToken(token)); + } + + final int indexOfActivityLocked(ActivityRecord r) { + return mHistory.indexOf(r); } final ActivityRecord isInStackLocked(IBinder token) { - try { - ActivityRecord r = (ActivityRecord)token; - if (mHistory.contains(r)) { - return r; - } - } catch (ClassCastException e) { - Slog.w(TAG, "Bad activity token: " + token, e); + ActivityRecord r = ActivityRecord.forToken(token); + if (mHistory.contains(r)) { + return r; } return null; } @@ -517,7 +516,7 @@ final class ActivityStack { throws RemoteException { r.startFreezingScreenLocked(app, 0); - mService.mWindowManager.setAppVisibility(r, true); + mService.mWindowManager.setAppVisibility(r.appToken, true); // Have the window manager re-evaluate the orientation of // the screen based on the new activity order. Note that @@ -528,8 +527,8 @@ final class ActivityStack { if (checkConfig) { Configuration config = mService.mWindowManager.updateOrientationFromAppTokens( mService.mConfiguration, - r.mayFreezeScreenLocked(app) ? r : null); - mService.updateConfigurationLocked(config, r, false); + r.mayFreezeScreenLocked(app) ? r.appToken : null); + mService.updateConfigurationLocked(config, r, false, false); } r.app = app; @@ -590,8 +589,9 @@ final class ActivityStack { profileFd = null; } } - app.thread.scheduleLaunchActivity(new Intent(r.intent), r, - System.identityHashCode(r), r.info, mService.mConfiguration, + app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, + System.identityHashCode(r), r.info, + new Configuration(mService.mConfiguration), r.compat, r.icicle, results, newIntents, !andResume, mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop); @@ -624,7 +624,7 @@ final class ActivityStack { + r.intent.getComponent().flattenToShortString() + ", giving up", e); mService.appDiedLocked(app, app.pid, app.thread); - requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null, + requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, "2nd-crash"); return false; } @@ -821,7 +821,7 @@ final class ActivityStack { } if (w > 0) { - return mService.mWindowManager.screenshotApplications(who, w, h); + return mService.mWindowManager.screenshotApplications(who.appToken, w, h); } return null; } @@ -856,8 +856,8 @@ final class ActivityStack { EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY, System.identityHashCode(prev), prev.shortComponentName); - prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving, - prev.configChangeFlags); + prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing, + userLeaving, prev.configChangeFlags); if (mMainStack) { mService.updateUsageStats(prev, false); } @@ -947,8 +947,24 @@ final class ActivityStack { r.state = ActivityState.STOPPED; if (!r.finishing) { if (r.configDestroy) { - destroyActivityLocked(r, true, false); + destroyActivityLocked(r, true, false, "stop-config"); resumeTopActivityLocked(null); + } else { + // Now that this process has stopped, we may want to consider + // it to be the previous app to try to keep around in case + // the user wants to return to it. + ProcessRecord fgApp = null; + if (mResumedActivity != null) { + fgApp = mResumedActivity.app; + } else if (mPausingActivity != null) { + fgApp = mPausingActivity.app; + } + if (r.app != null && fgApp != null && r.app != fgApp + && r.lastVisibleTime > mService.mPreviousProcessVisibleTime + && r.app != mService.mHomeProcess) { + mService.mPreviousProcess = r.app; + mService.mPreviousProcessVisibleTime = r.lastVisibleTime; + } } } } @@ -976,7 +992,7 @@ final class ActivityStack { // instance right now, we need to first completely stop // the current instance before starting the new one. if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev); - destroyActivityLocked(prev, true, false); + destroyActivityLocked(prev, true, false, "pause-config"); } else { mStoppingActivities.add(prev); if (mStoppingActivities.size() > 3) { @@ -1129,7 +1145,7 @@ final class ActivityStack { if (!r.visible) { if (DEBUG_VISBILITY) Slog.v( TAG, "Starting and making visible: " + r); - mService.mWindowManager.setAppVisibility(r, true); + mService.mWindowManager.setAppVisibility(r.appToken, true); } if (r != starting) { startSpecificActivityLocked(r, false, false); @@ -1153,10 +1169,10 @@ final class ActivityStack { if (DEBUG_VISBILITY) Slog.v( TAG, "Making visible and scheduling visibility: " + r); try { - mService.mWindowManager.setAppVisibility(r, true); + mService.mWindowManager.setAppVisibility(r.appToken, true); r.sleeping = false; r.app.pendingUiClean = true; - r.app.thread.scheduleWindowVisibility(r, true); + r.app.thread.scheduleWindowVisibility(r.appToken, true); r.stopFreezingScreenLocked(false); } catch (Exception e) { // Just skip on any failure; we'll make it @@ -1195,13 +1211,13 @@ final class ActivityStack { TAG, "Making invisible: " + r); r.visible = false; try { - mService.mWindowManager.setAppVisibility(r, false); + mService.mWindowManager.setAppVisibility(r.appToken, false); if ((r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) && r.app != null && r.app.thread != null) { if (DEBUG_VISBILITY) Slog.v( TAG, "Scheduling invisibility: " + r); - r.app.thread.scheduleWindowVisibility(r, false); + r.app.thread.scheduleWindowVisibility(r.appToken, false); } } catch (Exception e) { // Just skip on any failure; we'll make it @@ -1351,7 +1367,7 @@ final class ActivityStack { // previous should actually be hidden depending on whether the // new one is found to be full-screen or not. if (prev.finishing) { - mService.mWindowManager.setAppVisibility(prev, false); + mService.mWindowManager.setAppVisibility(prev.appToken, false); if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: " + prev + ", waitingVisible=" + (prev != null ? prev.waitingVisible : null) @@ -1391,8 +1407,8 @@ final class ActivityStack { ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE : WindowManagerPolicy.TRANSIT_TASK_CLOSE, false); } - mService.mWindowManager.setAppWillBeHidden(prev); - mService.mWindowManager.setAppVisibility(prev, false); + mService.mWindowManager.setAppWillBeHidden(prev.appToken); + mService.mWindowManager.setAppVisibility(prev.appToken, false); } else { if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: prev=" + prev); @@ -1406,8 +1422,8 @@ final class ActivityStack { } } if (false) { - mService.mWindowManager.setAppWillBeHidden(prev); - mService.mWindowManager.setAppVisibility(prev, false); + mService.mWindowManager.setAppWillBeHidden(prev.appToken); + mService.mWindowManager.setAppVisibility(prev.appToken, false); } } else if (mHistory.size() > 1) { if (DEBUG_TRANSITION) Slog.v(TAG, @@ -1425,7 +1441,7 @@ final class ActivityStack { if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next); // This activity is now becoming visible. - mService.mWindowManager.setAppVisibility(next, true); + mService.mWindowManager.setAppVisibility(next.appToken, true); ActivityRecord lastResumedActivity = mResumedActivity; ActivityState lastState = next.state; @@ -1449,11 +1465,11 @@ final class ActivityStack { synchronized (mService) { Configuration config = mService.mWindowManager.updateOrientationFromAppTokens( mService.mConfiguration, - next.mayFreezeScreenLocked(next.app) ? next : null); + next.mayFreezeScreenLocked(next.app) ? next.appToken : null); if (config != null) { next.frozenBeforeDestroy = true; } - updated = mService.updateConfigurationLocked(config, next, false); + updated = mService.updateConfigurationLocked(config, next, false, false); } } if (!updated) { @@ -1488,12 +1504,12 @@ final class ActivityStack { if (DEBUG_RESULTS) Slog.v( TAG, "Delivering results to " + next + ": " + a); - next.app.thread.scheduleSendResult(next, a); + next.app.thread.scheduleSendResult(next.appToken, a); } } if (next.newIntents != null) { - next.app.thread.scheduleNewIntent(next.newIntents, next); + next.app.thread.scheduleNewIntent(next.newIntents, next.appToken); } EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, @@ -1503,7 +1519,7 @@ final class ActivityStack { next.sleeping = false; showAskCompatModeDialogLocked(next); next.app.pendingUiClean = true; - next.app.thread.scheduleResumeActivity(next, + next.app.thread.scheduleResumeActivity(next.appToken, mService.isNextTransitionForward()); checkReadyForSleepLocked(); @@ -1520,7 +1536,7 @@ final class ActivityStack { } else { if (SHOW_APP_STARTING_PREVIEW && mMainStack) { mService.mWindowManager.setAppStartingWindow( - next, next.packageName, next.theme, + next.appToken, next.packageName, next.theme, mService.compatibilityInfoForPackageLocked( next.info.applicationInfo), next.nonLocalizedLabel, @@ -1541,7 +1557,7 @@ final class ActivityStack { // If any exception gets thrown, toss away this // activity and try the next one. Slog.w(TAG, "Exception thrown during resume of " + next, e); - requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null, + requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null, "resume-exception"); return true; } @@ -1559,7 +1575,7 @@ final class ActivityStack { } else { if (SHOW_APP_STARTING_PREVIEW) { mService.mWindowManager.setAppStartingWindow( - next, next.packageName, next.theme, + next.appToken, next.packageName, next.theme, mService.compatibilityInfoForPackageLocked( next.info.applicationInfo), next.nonLocalizedLabel, @@ -1602,10 +1618,10 @@ final class ActivityStack { } mHistory.add(addPos, r); r.putInHistory(); - mService.mWindowManager.addAppToken(addPos, r, r.task.taskId, + mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen); if (VALIDATE_TOKENS) { - mService.mWindowManager.validateAppTokens(mHistory); + validateAppTokensLocked(); } return; } @@ -1669,7 +1685,7 @@ final class ActivityStack { mNoAnimActivities.remove(r); } mService.mWindowManager.addAppToken( - addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); + addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen); boolean doShow = true; if (newTask) { // Even though this activity is starting fresh, we still need @@ -1697,19 +1713,20 @@ final class ActivityStack { else if (prev.nowVisible) prev = null; } mService.mWindowManager.setAppStartingWindow( - r, r.packageName, r.theme, + r.appToken, r.packageName, r.theme, mService.compatibilityInfoForPackageLocked( r.info.applicationInfo), r.nonLocalizedLabel, - r.labelRes, r.icon, r.windowFlags, prev, showStartingIcon); + r.labelRes, r.icon, r.windowFlags, + prev != null ? prev.appToken : null, showStartingIcon); } } else { // If this is the first activity, don't do any fancy animations, // because there is nothing for it to animate on top of. - mService.mWindowManager.addAppToken(addPos, r, r.task.taskId, + mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen); } if (VALIDATE_TOKENS) { - mService.mWindowManager.validateAppTokens(mHistory); + validateAppTokensLocked(); } if (doResume) { @@ -1717,6 +1734,15 @@ final class ActivityStack { } } + final void validateAppTokensLocked() { + mValidateAppTokens.clear(); + mValidateAppTokens.ensureCapacity(mHistory.size()); + for (int i=0; i<mHistory.size(); i++) { + mValidateAppTokens.add(mHistory.get(i).appToken); + } + mService.mWindowManager.validateAppTokens(mValidateAppTokens); + } + /** * Perform a reset of the given task, if needed as part of launching it. * Returns the new HistoryRecord at the top of the task. @@ -1818,7 +1844,7 @@ final class ActivityStack { if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + " out to new task " + target.task); } - mService.mWindowManager.setAppGroupId(target, task.taskId); + mService.mWindowManager.setAppGroupId(target.appToken, task.taskId); if (replyChainEnd < 0) { replyChainEnd = targetI; } @@ -1841,11 +1867,11 @@ final class ActivityStack { } mHistory.remove(srcPos); mHistory.add(dstPos, p); - mService.mWindowManager.moveAppToken(dstPos, p); - mService.mWindowManager.setAppGroupId(p, p.task.taskId); + mService.mWindowManager.moveAppToken(dstPos, p.appToken); + mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId); dstPos++; if (VALIDATE_TOKENS) { - mService.mWindowManager.validateAppTokens(mHistory); + validateAppTokensLocked(); } i++; } @@ -1977,10 +2003,10 @@ final class ActivityStack { mHistory.add(lastReparentPos, p); if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " in to resetting task " + task); - mService.mWindowManager.moveAppToken(lastReparentPos, p); - mService.mWindowManager.setAppGroupId(p, p.task.taskId); + mService.mWindowManager.moveAppToken(lastReparentPos, p.appToken); + mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId); if (VALIDATE_TOKENS) { - mService.mWindowManager.validateAppTokens(mHistory); + validateAppTokensLocked(); } } replyChainEnd = -1; @@ -2073,7 +2099,7 @@ final class ActivityStack { if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { if (!ret.finishing) { - int index = indexOfTokenLocked(ret); + int index = indexOfTokenLocked(ret.appToken); if (index >= 0) { finishActivityLocked(ret, index, Activity.RESULT_CANCELED, null, "clear"); @@ -2900,7 +2926,7 @@ final class ActivityStack { mConfigWillChange = false; if (DEBUG_CONFIGURATION) Slog.v(TAG, "Updating to new configuration after starting activity."); - mService.updateConfigurationLocked(config, null, false); + mService.updateConfigurationLocked(config, null, false, false); } Binder.restoreCallingIdentity(origId); @@ -2999,7 +3025,7 @@ final class ActivityStack { return res; } - resultTo = outActivity[0]; + resultTo = outActivity[0] != null ? outActivity[0].appToken : null; } } } finally { @@ -3057,7 +3083,7 @@ final class ActivityStack { ArrayList<ResultInfo> list = new ArrayList<ResultInfo>(); list.add(new ResultInfo(resultWho, requestCode, resultCode, data)); - r.app.thread.scheduleSendResult(r, list); + r.app.thread.scheduleSendResult(r.appToken, list); return; } catch (Exception e) { Slog.w(TAG, "Exception thrown sending result to " + r, e); @@ -3072,7 +3098,7 @@ final class ActivityStack { if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) { if (!r.finishing) { - requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null, + requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, "no-history"); } } else if (r.app != null && r.app.thread != null) { @@ -3090,9 +3116,9 @@ final class ActivityStack { if (DEBUG_VISBILITY) Slog.v( TAG, "Stopping visible=" + r.visible + " for " + r); if (!r.visible) { - mService.mWindowManager.setAppVisibility(r, false); + mService.mWindowManager.setAppVisibility(r.appToken, false); } - r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags); + r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags); if (mService.isSleeping()) { r.setSleeping(true); } @@ -3106,7 +3132,7 @@ final class ActivityStack { if (DEBUG_STATES) Slog.v(TAG, "Stop failed; moving to STOPPED: " + r); r.state = ActivityState.STOPPED; if (r.configDestroy) { - destroyActivityLocked(r, true, false); + destroyActivityLocked(r, true, false, "stop-except"); } } } @@ -3137,7 +3163,7 @@ final class ActivityStack { // normal flow and hide it once we determine that it is // hidden by the activities in front of it. if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s); - mService.mWindowManager.setAppVisibility(s, false); + mService.mWindowManager.setAppVisibility(s.appToken, false); } } if ((!s.waitingVisible || mService.isSleeping()) && remove) { @@ -3178,14 +3204,14 @@ final class ActivityStack { boolean enableScreen = false; synchronized (mService) { - if (token != null) { - mHandler.removeMessages(IDLE_TIMEOUT_MSG, token); + ActivityRecord r = ActivityRecord.forToken(token); + if (r != null) { + mHandler.removeMessages(IDLE_TIMEOUT_MSG, r); } // Get the activity record. - int index = indexOfTokenLocked(token); + int index = indexOfActivityLocked(r); if (index >= 0) { - ActivityRecord r = mHistory.get(index); res = r; if (fromTimeout) { @@ -3281,7 +3307,7 @@ final class ActivityStack { for (i=0; i<NF; i++) { ActivityRecord r = (ActivityRecord)finishes.get(i); synchronized (mService) { - destroyActivityLocked(r, true, false); + destroyActivityLocked(r, true, false, "finish-idle"); } } @@ -3405,7 +3431,7 @@ final class ActivityStack { : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE, false); // Tell window manager to prepare for this one to be removed. - mService.mWindowManager.setAppVisibility(r, false); + mService.mWindowManager.setAppVisibility(r.appToken, false); if (mPausingActivity == null) { if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r); @@ -3432,7 +3458,7 @@ final class ActivityStack { private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode) { - final int index = indexOfTokenLocked(r); + final int index = indexOfActivityLocked(r); if (index < 0) { return null; } @@ -3480,7 +3506,7 @@ final class ActivityStack { || prevState == ActivityState.INITIALIZING) { // If this activity is already stopped, we can just finish // it right now. - return destroyActivityLocked(r, true, true) ? null : r; + return destroyActivityLocked(r, true, true, "finish-imm") ? null : r; } else { // Need to go through the full pause cycle to get this // activity into the stopped state and then finish it. @@ -3562,9 +3588,9 @@ final class ActivityStack { if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (removed from history)"); r.state = ActivityState.DESTROYED; - mService.mWindowManager.removeAppToken(r); + mService.mWindowManager.removeAppToken(r.appToken); if (VALIDATE_TOKENS) { - mService.mWindowManager.validateAppTokens(mHistory); + validateAppTokensLocked(); } cleanUpActivityServicesLocked(r); r.removeUriPermissionsLocked(); @@ -3586,7 +3612,7 @@ final class ActivityStack { } } - final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj) { + final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj, String reason) { for (int i=mHistory.size()-1; i>=0; i--) { ActivityRecord r = mHistory.get(i); if (owner != null && r.app != owner) { @@ -3597,7 +3623,7 @@ final class ActivityStack { if (r.app != null && r.haveState && !r.visible && r.stopped && !r.finishing && r.state != ActivityState.DESTROYING && r.state != ActivityState.DESTROYED) { - destroyActivityLocked(r, true, oomAdj); + destroyActivityLocked(r, true, oomAdj, "trim"); } } } @@ -3609,13 +3635,13 @@ final class ActivityStack { * but then create a new client-side object for this same HistoryRecord. */ final boolean destroyActivityLocked(ActivityRecord r, - boolean removeFromApp, boolean oomAdj) { + boolean removeFromApp, boolean oomAdj, String reason) { if (DEBUG_SWITCH) Slog.v( TAG, "Removing activity: token=" + r + ", app=" + (r.app != null ? r.app.processName : "(null)")); EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, System.identityHashCode(r), - r.task.taskId, r.shortComponentName); + r.task.taskId, r.shortComponentName, reason); boolean removedFromHistory = false; @@ -3645,7 +3671,7 @@ final class ActivityStack { try { if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r); - r.app.thread.scheduleDestroyActivity(r, r.finishing, + r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing, r.configChangeFlags); } catch (Exception e) { // We can just ignore exceptions here... if the process @@ -3704,11 +3730,13 @@ final class ActivityStack { final void activityDestroyed(IBinder token) { synchronized (mService) { - mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token); + ActivityRecord r = ActivityRecord.forToken(token); + if (r != null) { + mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r); + } - int index = indexOfTokenLocked(token); + int index = indexOfActivityLocked(r); if (index >= 0) { - ActivityRecord r = mHistory.get(index); if (r.state == ActivityState.DESTROYING) { final long origId = Binder.clearCallingIdentity(); removeActivityFromHistoryLocked(r); @@ -3773,7 +3801,7 @@ final class ActivityStack { return; } - ArrayList moved = new ArrayList(); + ArrayList<IBinder> moved = new ArrayList<IBinder>(); // Applying the affinities may have removed entries from the history, // so get the size again. @@ -3795,7 +3823,7 @@ final class ActivityStack { } mHistory.remove(pos); mHistory.add(top, r); - moved.add(0, r); + moved.add(0, r.appToken); top--; } pos--; @@ -3818,7 +3846,7 @@ final class ActivityStack { mService.mWindowManager.moveAppTokensToTop(moved); if (VALIDATE_TOKENS) { - mService.mWindowManager.validateAppTokens(mHistory); + validateAppTokensLocked(); } finishTaskMoveLocked(task); @@ -3865,7 +3893,7 @@ final class ActivityStack { } } - ArrayList moved = new ArrayList(); + ArrayList<IBinder> moved = new ArrayList<IBinder>(); if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to back transition: task=" + task); @@ -3890,7 +3918,7 @@ final class ActivityStack { } mHistory.remove(pos); mHistory.add(bottom, r); - moved.add(r); + moved.add(r.appToken); bottom++; } pos++; @@ -3910,7 +3938,7 @@ final class ActivityStack { } mService.mWindowManager.moveAppTokensToBottom(moved); if (VALIDATE_TOKENS) { - mService.mWindowManager.validateAppTokens(mHistory); + validateAppTokensLocked(); } finishTaskMoveLocked(task); @@ -4102,7 +4130,7 @@ final class ActivityStack { if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is destroying non-running " + r); - destroyActivityLocked(r, true, false); + destroyActivityLocked(r, true, false, "config"); } else if (r.state == ActivityState.PAUSING) { // A little annoying: we are waiting for this activity to // finish pausing. Let's not do anything now, but just @@ -4140,7 +4168,7 @@ final class ActivityStack { if (r.app != null && r.app.thread != null) { try { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r); - r.app.thread.scheduleActivityConfigurationChanged(r); + r.app.thread.scheduleActivityConfigurationChanged(r.appToken); } catch (RemoteException e) { // If process died, whatever. } @@ -4170,8 +4198,8 @@ final class ActivityStack { try { if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r); r.forceNewConfig = false; - r.app.thread.scheduleRelaunchActivity(r, results, newIntents, - changes, !andResume, mService.mConfiguration); + r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, + changes, !andResume, new Configuration(mService.mConfiguration)); // Note: don't need to call pauseIfSleepingLocked() here, because // the caller will only pass in 'andResume' if this activity is // currently resumed, which implies we aren't sleeping. diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java index 9c55597..3835553 100644 --- a/services/java/com/android/server/am/ContentProviderRecord.java +++ b/services/java/com/android/server/am/ContentProviderRecord.java @@ -73,12 +73,15 @@ class ContentProviderRecord extends ContentProviderHolder { pw.print("multiprocess="); pw.print(info.multiprocess); pw.print(" initOrder="); pw.println(info.initOrder); } - if (clients.size() > 0) { - pw.print(prefix); pw.print("clients="); pw.println(clients); - } if (externals != 0) { pw.print(prefix); pw.print("externals="); pw.println(externals); } + if (clients.size() > 0) { + pw.print(prefix); pw.println("Clients:"); + for (ProcessRecord cproc : clients) { + pw.print(prefix); pw.print(" - "); pw.println(cproc.toShortString()); + } + } } public String toString() { diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags index aadd37d..a579f44 100644 --- a/services/java/com/android/server/am/EventLogTags.logtags +++ b/services/java/com/android/server/am/EventLogTags.logtags @@ -48,7 +48,7 @@ option java_package com.android.server.am # Reporting to applications that memory is low 30017 am_low_memory (Num Processes|1|1) # An activity is being destroyed: -30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3) +30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3) # An activity has been relaunched, resumed, and is now in the foreground: 30019 am_relaunch_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3) # An activity has been relaunched: diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index 131255f..af7b314 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -16,7 +16,6 @@ package com.android.server.am; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -24,7 +23,6 @@ import com.android.internal.util.MemInfoReader; import com.android.server.wm.WindowManagerService; import android.graphics.Point; -import android.os.StrictMode; import android.util.Slog; /** @@ -37,27 +35,31 @@ class ProcessList { // OOM adjustments for processes in various states: - // This is a process without anything currently running in it. Definitely - // the first to go! Value set in system/rootdir/init.rc on startup. - // This value is initalized in the constructor, careful when refering to - // this static variable externally. - static final int EMPTY_APP_ADJ = 15; - // This is a process only hosting activities that are not visible, - // so it can be killed without any disruption. Value set in - // system/rootdir/init.rc on startup. + // so it can be killed without any disruption. static final int HIDDEN_APP_MAX_ADJ = 15; - static int HIDDEN_APP_MIN_ADJ = 7; + static int HIDDEN_APP_MIN_ADJ = 9; + + // The B list of SERVICE_ADJ -- these are the old and decrepit + // services that aren't as shiny and interesting as the ones in the A list. + static final int SERVICE_B_ADJ = 8; + + // This is the process of the previous application that the user was in. + // This process is kept above other things, because it is very common to + // switch back to the previous app. This is important both for recent + // task switch (toggling between the two top recent apps) as well as normal + // UI flow such as clicking on a URI in the e-mail app to view in the browser, + // and then pressing back to return to e-mail. + static final int PREVIOUS_APP_ADJ = 7; // This is a process holding the home application -- we want to try // avoiding killing it, even if it would normally be in the background, // because the user interacts with it so much. static final int HOME_APP_ADJ = 6; - // This is a process holding a secondary server -- killing it will not - // have much of an impact as far as the user is concerned. Value set in - // system/rootdir/init.rc on startup. - static final int SECONDARY_SERVER_ADJ = 5; + // This is a process holding an application service -- killing it will not + // have much of an impact as far as the user is concerned. + static final int SERVICE_ADJ = 5; // This is a process currently hosting a backup operation. Killing it // is not entirely fatal but is generally a bad idea. @@ -70,22 +72,20 @@ class ProcessList { // This is a process only hosting components that are perceptible to the // user, and we really want to avoid killing them, but they are not - // immediately visible. An example is background music playback. Value set in - // system/rootdir/init.rc on startup. + // immediately visible. An example is background music playback. static final int PERCEPTIBLE_APP_ADJ = 2; // This is a process only hosting activities that are visible to the - // user, so we'd prefer they don't disappear. Value set in - // system/rootdir/init.rc on startup. + // user, so we'd prefer they don't disappear. static final int VISIBLE_APP_ADJ = 1; // This is the process running the current foreground app. We'd really - // rather not kill it! Value set in system/rootdir/init.rc on startup. + // rather not kill it! static final int FOREGROUND_APP_ADJ = 0; - // This is a process running a core server, such as telephony. Definitely + // This is a system persistent process, such as telephony. Definitely // don't want to kill it, but doing so is not completely fatal. - static final int CORE_SERVER_ADJ = -12; + static final int PERSISTENT_PROC_ADJ = -12; // The system process runs at the default adjustment. static final int SYSTEM_ADJ = -16; @@ -115,7 +115,7 @@ class ProcessList { // can't give it a different value for every possible kind of process. private final int[] mOomAdj = new int[] { FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ, - BACKUP_APP_ADJ, HIDDEN_APP_MIN_ADJ, EMPTY_APP_ADJ + BACKUP_APP_ADJ, HIDDEN_APP_MIN_ADJ, HIDDEN_APP_MAX_ADJ }; // These are the low-end OOM level limits. This is appropriate for an // HVGA or smaller phone with less than 512MB. Values are in KB. diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 9392bb4..72292be 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -63,6 +63,7 @@ class ProcessRecord { int curSchedGroup; // Currently desired scheduling class int setSchedGroup; // Last set to background scheduling class int trimMemoryLevel; // Last selected memory trimming level + boolean serviceb; // Process currently is on the service B list boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running any services that are foreground? @@ -179,6 +180,7 @@ class ProcessRecord { pw.print(prefix); pw.print("lastActivityTime="); TimeUtils.formatDuration(lastActivityTime, now, pw); pw.print(" lruWeight="); pw.print(lruWeight); + pw.print(" serviceb="); pw.print(serviceb); pw.print(" keeping="); pw.print(keeping); pw.print(" hidden="); pw.print(hidden); pw.print(" empty="); pw.println(empty); @@ -271,7 +273,7 @@ class ProcessRecord { processName = _processName; pkgList.add(_info.packageName); thread = _thread; - maxAdj = ProcessList.EMPTY_APP_ADJ; + maxAdj = ProcessList.HIDDEN_APP_MAX_ADJ; hiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ; curRawAdj = setRawAdj = -100; curAdj = setAdj = -100; diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index a860763..de3129b 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -54,8 +54,17 @@ class TaskRecord extends ThumbnailHolder { void setIntent(Intent _intent, ActivityInfo info) { stringName = null; - + if (info.targetActivity == null) { + if (_intent != null) { + // If this Intent has a selector, we want to clear it for the + // recent task since it is not relevant if the user later wants + // to re-launch the app. + if (_intent.getSelector() != null) { + _intent = new Intent(_intent); + _intent.setSelector(null); + } + } intent = _intent; realActivity = _intent != null ? _intent.getComponent() : null; origActivity = null; @@ -65,6 +74,7 @@ class TaskRecord extends ThumbnailHolder { if (_intent != null) { Intent targetIntent = new Intent(_intent); targetIntent.setComponent(targetComponent); + targetIntent.setSelector(null); intent = targetIntent; realActivity = targetComponent; origActivity = _intent.getComponent(); diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 423a78f..c344bc6 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -73,7 +73,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private Context mContext; private final static String TAG = "Tethering"; private final static boolean DBG = true; - private final static boolean VDBG = true; + private final static boolean VDBG = false; // TODO - remove both of these - should be part of interface inspection/selection stuff private String[] mTetherableUsbRegexs; @@ -228,7 +228,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if (isUsb(iface)) { // ignore usb0 down after enabling RNDIS // we will handle disconnect in interfaceRemoved instead - if (VDBG) Log.d(TAG, "ignoring interface down for " + iface); + if (VDBG) Log.d(TAG, "ignore interface down for " + iface); } else if (sm != null) { sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN); mIfaces.remove(iface); @@ -298,7 +298,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mIfaces.put(iface, sm); sm.start(); } - if (VDBG) Log.d(TAG, "interfaceAdded :" + iface); } public void interfaceRemoved(String iface) { @@ -415,7 +414,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER, erroredList); mContext.sendStickyBroadcast(broadcast); - if (VDBG) { + if (DBG) { Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " + activeList.size() + ", " + erroredList.size()); } @@ -865,7 +864,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { @Override public boolean processMessage(Message message) { - if (VDBG) Log.d(TAG, "InitialState.processMessage what=" + message.what); + if (DBG) Log.d(TAG, "InitialState.processMessage what=" + message.what); boolean retValue = true; switch (message.what) { case CMD_TETHER_REQUESTED: @@ -906,7 +905,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } @Override public boolean processMessage(Message message) { - if (VDBG) Log.d(TAG, "StartingState.processMessage what=" + message.what); + if (DBG) Log.d(TAG, "StartingState.processMessage what=" + message.what); boolean retValue = true; switch (message.what) { // maybe a parent class? @@ -985,7 +984,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { @Override public boolean processMessage(Message message) { - if (VDBG) Log.d(TAG, "TetheredState.processMessage what=" + message.what); + if (DBG) Log.d(TAG, "TetheredState.processMessage what=" + message.what); boolean retValue = true; boolean error = false; switch (message.what) { @@ -1061,7 +1060,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { ConnectivityManager.TETHER_ERROR_MASTER_ERROR); break; } - if (VDBG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName); + if (DBG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName); sendTetherStateChangedBroadcast(); if (mUsb) { if (!Tethering.this.configureUsbIface(false)) { @@ -1296,7 +1295,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } - if (VDBG) { + if (DBG) { Log.d(TAG, "chooseUpstreamType(" + tryCell + "), preferredApn =" + mPreferredUpstreamMobileApn + ", got type=" + upType); } @@ -1328,7 +1327,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } protected void notifyTetheredOfNewUpstreamIface(String ifaceName) { - if (VDBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName); + if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName); mUpstreamIfaceName = ifaceName; for (Object o : mNotifyList) { TetherInterfaceSM sm = (TetherInterfaceSM)o; @@ -1344,7 +1343,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } @Override public boolean processMessage(Message message) { - if (VDBG) Log.d(TAG, "MasterInitialState.processMessage what=" + message.what); + if (DBG) Log.d(TAG, "MasterInitialState.processMessage what=" + message.what); boolean retValue = true; switch (message.what) { case CMD_TETHER_MODE_REQUESTED: @@ -1386,7 +1385,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } @Override public boolean processMessage(Message message) { - if (VDBG) Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what); + if (DBG) Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what); boolean retValue = true; switch (message.what) { case CMD_TETHER_MODE_REQUESTED: diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index bdad82a..8c0f1e0 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -19,7 +19,6 @@ package com.android.server.net; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.DUMP; -import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.READ_PHONE_STATE; @@ -93,6 +92,7 @@ import android.os.HandlerThread; import android.os.INetworkManagementService; import android.os.IPowerManager; import android.os.Message; +import android.os.MessageQueue.IdleHandler; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.provider.Settings; @@ -190,6 +190,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_METERED_IFACES_CHANGED = 2; private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 3; private static final int MSG_PROCESS_DIED = 4; + private static final int MSG_LIMIT_REACHED = 5; private final Context mContext; private final IActivityManager mActivityManager; @@ -225,8 +226,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Set of currently active {@link Notification} tags. */ private HashSet<String> mActiveNotifs = Sets.newHashSet(); - /** Current values from {@link #setPolicyDataEnable(int, boolean)}. */ - private SparseBooleanArray mActiveNetworkEnabled = new SparseBooleanArray(); /** Foreground at both UID and PID granularity. */ private SparseBooleanArray mUidForeground = new SparseBooleanArray(); @@ -394,6 +393,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // on background handler thread, and verified // READ_NETWORK_USAGE_HISTORY permission above. + maybeRefreshTrustedTime(); synchronized (mRulesLock) { updateNetworkEnabledLocked(); updateNotificationsLocked(); @@ -424,19 +424,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like NMS should be calling us mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - synchronized (mRulesLock) { - if (mMeteredIfaces.contains(iface) && !LIMIT_GLOBAL_ALERT.equals(limitName)) { - try { - // force stats update to make sure we have numbers that - // caused alert to trigger. - mNetworkStats.forceUpdate(); - } catch (RemoteException e) { - // ignored; service lives in system_server - } - - updateNetworkEnabledLocked(); - updateNotificationsLocked(); - } + if (!LIMIT_GLOBAL_ALERT.equals(limitName)) { + mHandler.obtainMessage(MSG_LIMIT_REACHED, iface).sendToTarget(); } } }; @@ -457,7 +446,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // cycle boundary to recompute notifications. // examine stats for each active policy - final long currentTime = currentTimeMillis(true); + final long currentTime = currentTimeMillis(); for (NetworkPolicy policy : mNetworkPolicy.values()) { // ignore policies that aren't relevant to user if (!isTemplateRelevant(policy.template)) continue; @@ -695,6 +684,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void onReceive(Context context, Intent intent) { // on background handler thread, and verified CONNECTIVITY_INTERNAL // permission above. + + maybeRefreshTrustedTime(); synchronized (mRulesLock) { ensureActiveMobilePolicyLocked(); updateNetworkEnabledLocked(); @@ -714,7 +705,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: reset any policy-disabled networks when any policy is removed // completely, which is currently rare case. - final long currentTime = currentTimeMillis(true); + final long currentTime = currentTimeMillis(); for (NetworkPolicy policy : mNetworkPolicy.values()) { // shortcut when policy has no limit if (policy.limitBytes == LIMIT_DISABLED) { @@ -814,7 +805,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // apply each policy that we found ifaces for; compute remaining data // based on current cycle and historical stats, and push to kernel. - final long currentTime = currentTimeMillis(true); + final long currentTime = currentTimeMillis(); for (NetworkPolicy policy : mNetworkRules.keySet()) { final String[] ifaces = mNetworkRules.get(policy); @@ -1104,6 +1095,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void setNetworkPolicies(NetworkPolicy[] policies) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + maybeRefreshTrustedTime(); synchronized (mRulesLock) { mNetworkPolicy.clear(); for (NetworkPolicy policy : policies) { @@ -1131,7 +1123,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void snoozePolicy(NetworkTemplate template) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - final long currentTime = currentTimeMillis(true); + maybeRefreshTrustedTime(); + final long currentTime = currentTimeMillis(); synchronized (mRulesLock) { // find and snooze local policy that matches final NetworkPolicy policy = mNetworkPolicy.get(template); @@ -1152,6 +1145,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void setRestrictBackground(boolean restrictBackground) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + maybeRefreshTrustedTime(); synchronized (mRulesLock) { mRestrictBackground = restrictBackground; updateRulesForRestrictBackgroundLocked(); @@ -1205,7 +1199,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return null; } - final long currentTime = currentTimeMillis(false); + final long currentTime = currentTimeMillis(); // find total bytes used under policy final long start = computeLastCycleBoundary(currentTime, policy); @@ -1481,6 +1475,26 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } return true; } + case MSG_LIMIT_REACHED: { + final String iface = (String) msg.obj; + + maybeRefreshTrustedTime(); + synchronized (mRulesLock) { + if (mMeteredIfaces.contains(iface)) { + try { + // force stats update to make sure we have + // numbers that caused alert to trigger. + mNetworkStats.forceUpdate(); + } catch (RemoteException e) { + // ignored; service lives in system_server + } + + updateNetworkEnabledLocked(); + updateNotificationsLocked(); + } + } + return true; + } default: { return false; } @@ -1519,21 +1533,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** - * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)}, - * dispatching only when actually changed. + * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)}. */ private void setPolicyDataEnable(int networkType, boolean enabled) { - synchronized (mActiveNetworkEnabled) { - final boolean prevEnabled = mActiveNetworkEnabled.get(networkType, true); - if (prevEnabled == enabled) return; - - try { - mConnManager.setPolicyDataEnable(networkType, enabled); - } catch (RemoteException e) { - // ignored; service lives in system_server - } - - mActiveNetworkEnabled.put(networkType, enabled); + try { + mConnManager.setPolicyDataEnable(networkType, enabled); + } catch (RemoteException e) { + // ignored; service lives in system_server } } @@ -1552,12 +1558,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private long currentTimeMillis(boolean allowRefresh) { - // try refreshing time source when stale - if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE && allowRefresh) { + /** + * Try refreshing {@link #mTime} when stale. + */ + private void maybeRefreshTrustedTime() { + if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) { mTime.forceRefresh(); } + } + private long currentTimeMillis() { return mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); } @@ -1583,6 +1593,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return intent; } + // @VisibleForTesting + public void addIdleHandler(IdleHandler handler) { + mHandler.getLooper().getQueue().addIdleHandler(handler); + } + private static void collectKeys(SparseIntArray source, SparseBooleanArray target) { final int size = source.size(); for (int i = 0; i < size; i++) { diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 789681e..871ed68 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -32,7 +32,6 @@ import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.SET_FOREGROUND; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStatsHistory.randomLong; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateWifi; import static android.net.TrafficStats.UID_REMOVED; @@ -49,7 +48,6 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS; -import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats; @@ -73,9 +71,11 @@ import android.net.NetworkIdentity; import android.net.NetworkInfo; import android.net.NetworkState; import android.net.NetworkStats; +import android.net.NetworkStats.NonMonotonicException; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.os.Binder; +import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -150,6 +150,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Sample recent usage after each poll event. */ private static final boolean ENABLE_SAMPLE_AFTER_POLL = true; + private static final String TAG_NETSTATS_ERROR = "netstats_error"; + private final Context mContext; private final INetworkManagementService mNetworkManager; private final IAlarmManager mAlarmManager; @@ -160,6 +162,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final PowerManager.WakeLock mWakeLock; private IConnectivityManager mConnManager; + private DropBoxManager mDropBox; // @VisibleForTesting public static final String ACTION_NETWORK_STATS_POLL = @@ -200,6 +203,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Set of historical {@code xtables} stats for known UIDs. */ private HashMap<UidStatsKey, NetworkStatsHistory> mUidStats = Maps.newHashMap(); + /** Flag if {@link #mNetworkDevStats} have been loaded from disk. */ + private boolean mNetworkStatsLoaded = false; /** Flag if {@link #mUidStats} have been loaded from disk. */ private boolean mUidStatsLoaded = false; @@ -269,8 +274,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // until actually needed. readNetworkDevStatsLocked(); readNetworkXtStatsLocked(); + mNetworkStatsLoaded = true; } + // bootstrap initial stats to prevent double-counting later + bootstrapStats(); + // watch for network interfaces to be claimed final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE); mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler); @@ -304,8 +313,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { registerPollAlarmLocked(); registerGlobalAlert(); - // bootstrap initial stats to prevent double-counting later - bootstrapStats(); + mDropBox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); } private void shutdownLocked() { @@ -317,14 +325,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mTeleManager.listen(mPhoneListener, LISTEN_NONE); - writeNetworkDevStatsLocked(); - writeNetworkXtStatsLocked(); + if (mNetworkStatsLoaded) { + writeNetworkDevStatsLocked(); + writeNetworkXtStatsLocked(); + } if (mUidStatsLoaded) { writeUidStatsLocked(); } mNetworkDevStats.clear(); mNetworkXtStats.clear(); mUidStats.clear(); + mNetworkStatsLoaded = false; mUidStatsLoaded = false; } @@ -467,6 +478,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + private long getHistoryStartLocked( + NetworkTemplate template, HashMap<NetworkIdentitySet, NetworkStatsHistory> source) { + long start = Long.MAX_VALUE; + for (NetworkIdentitySet ident : source.keySet()) { + if (templateMatches(template, ident)) { + final NetworkStatsHistory history = source.get(ident); + start = Math.min(start, history.getStart()); + } + } + return start; + } + @Override public NetworkStats getSummaryForAllUid( NetworkTemplate template, long start, long end, boolean includeTags) { @@ -621,7 +644,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // broadcast. final int uid = intent.getIntExtra(EXTRA_UID, 0); synchronized (mStatsLock) { - // TODO: perform one last stats poll for UID mWakeLock.acquire(); try { removeUidLocked(uid); @@ -767,6 +789,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void performPoll(int flags) { synchronized (mStatsLock) { mWakeLock.acquire(); + + // try refreshing time source when stale + if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) { + mTime.forceRefresh(); + } + try { performPollLocked(flags); } finally { @@ -787,11 +815,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0; final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0; - // try refreshing time source when stale - if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) { - mTime.forceRefresh(); - } - // TODO: consider marking "untrusted" times in historical stats final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); @@ -802,9 +825,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats networkDevSnapshot; try { // collect any tethering stats - final String[] tetheredIfacePairs = mConnManager.getTetheredIfacePairs(); - final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering( - tetheredIfacePairs); + final NetworkStats tetherSnapshot = getNetworkStatsTethering(); // record uid stats, folding in tethering stats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL); @@ -829,9 +850,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // persist when enough network data has occurred final long persistNetworkDevDelta = computeStatsDelta( - mLastPersistNetworkDevSnapshot, networkDevSnapshot, true).getTotalBytes(); + mLastPersistNetworkDevSnapshot, networkDevSnapshot, true, "devp").getTotalBytes(); final long persistNetworkXtDelta = computeStatsDelta( - mLastPersistNetworkXtSnapshot, networkXtSnapshot, true).getTotalBytes(); + mLastPersistNetworkXtSnapshot, networkXtSnapshot, true, "xtp").getTotalBytes(); final boolean networkOverThreshold = persistNetworkDevDelta > threshold || persistNetworkXtDelta > threshold; if (persistForce || (persistNetwork && networkOverThreshold)) { @@ -842,8 +863,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } // persist when enough uid data has occurred - final long persistUidDelta = computeStatsDelta(mLastPersistUidSnapshot, uidSnapshot, true) - .getTotalBytes(); + final long persistUidDelta = computeStatsDelta( + mLastPersistUidSnapshot, uidSnapshot, true, "uidp").getTotalBytes(); if (persistForce || (persistUid && persistUidDelta > threshold)) { writeUidStatsLocked(); mLastPersistUidSnapshot = uidSnapshot; @@ -872,7 +893,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final HashSet<String> unknownIface = Sets.newHashSet(); final NetworkStats delta = computeStatsDelta( - mLastPollNetworkDevSnapshot, networkDevSnapshot, false); + mLastPollNetworkDevSnapshot, networkDevSnapshot, false, "dev"); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; @@ -902,7 +923,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final HashSet<String> unknownIface = Sets.newHashSet(); final NetworkStats delta = computeStatsDelta( - mLastPollNetworkXtSnapshot, networkXtSnapshot, false); + mLastPollNetworkXtSnapshot, networkXtSnapshot, false, "xt"); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; @@ -931,9 +952,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) { ensureUidStatsLoadedLocked(); - final NetworkStats delta = computeStatsDelta(mLastPollUidSnapshot, uidSnapshot, false); + final NetworkStats delta = computeStatsDelta( + mLastPollUidSnapshot, uidSnapshot, false, "uid"); final NetworkStats operationsDelta = computeStatsDelta( - mLastPollOperationsSnapshot, mOperations, false); + mLastPollOperationsSnapshot, mOperations, false, "uidop"); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; @@ -962,8 +984,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } mLastPollUidSnapshot = uidSnapshot; - mLastPollOperationsSnapshot = mOperations; - mOperations = new NetworkStats(0L, 10); + mLastPollOperationsSnapshot = mOperations.clone(); } /** @@ -979,6 +1000,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long start = end - largestBucketSize; final long trustedTime = mTime.hasCache() ? mTime.currentTimeMillis() : -1; + long devHistoryStart = Long.MAX_VALUE; NetworkTemplate template = null; NetworkStats.Entry devTotal = null; @@ -988,24 +1010,27 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // collect mobile sample template = buildTemplateMobileAll(getActiveSubscriberId(mContext)); devTotal = getSummaryForNetworkDev(template, start, end).getTotal(devTotal); + devHistoryStart = getHistoryStartLocked(template, mNetworkDevStats); xtTotal = getSummaryForNetworkXt(template, start, end).getTotal(xtTotal); uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal); + EventLogTags.writeNetstatsMobileSample( devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets, xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets, - trustedTime); + trustedTime, devHistoryStart); // collect wifi sample template = buildTemplateWifi(); devTotal = getSummaryForNetworkDev(template, start, end).getTotal(devTotal); + devHistoryStart = getHistoryStartLocked(template, mNetworkDevStats); xtTotal = getSummaryForNetworkXt(template, start, end).getTotal(xtTotal); uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal); EventLogTags.writeNetstatsWifiSample( devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets, xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets, - trustedTime); + trustedTime, devHistoryStart); } /** @@ -1014,6 +1039,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void removeUidLocked(int uid) { ensureUidStatsLoadedLocked(); + // perform one last poll before removing + performPollLocked(FLAG_PERSIST_ALL); + final ArrayList<UidStatsKey> knownKeys = Lists.newArrayList(); knownKeys.addAll(mUidStats.keySet()); @@ -1031,6 +1059,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + // clear UID from current stats snapshot + mLastPollUidSnapshot = mLastPollUidSnapshot.withoutUid(uid); + mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot); + // clear kernel stats associated with UID resetKernelUidStats(uid); @@ -1234,10 +1266,28 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // trim any history beyond max if (mTime.hasCache()) { - final long currentTime = mTime.currentTimeMillis(); + final long systemCurrentTime = System.currentTimeMillis(); + final long trustedCurrentTime = mTime.currentTimeMillis(); + + final long currentTime = Math.min(systemCurrentTime, trustedCurrentTime); final long maxHistory = mSettings.getNetworkMaxHistory(); + for (NetworkStatsHistory history : input.values()) { + final int beforeSize = history.size(); history.removeBucketsBefore(currentTime - maxHistory); + final int afterSize = history.size(); + + if (beforeSize > 24 && afterSize < beforeSize / 2) { + // yikes, dropping more than half of significant history + final StringBuilder builder = new StringBuilder(); + builder.append("yikes, dropping more than half of history").append('\n'); + builder.append("systemCurrentTime=").append(systemCurrentTime).append('\n'); + builder.append("trustedCurrentTime=").append(trustedCurrentTime).append('\n'); + builder.append("maxHistory=").append(maxHistory).append('\n'); + builder.append("beforeSize=").append(beforeSize).append('\n'); + builder.append("afterSize=").append(afterSize).append('\n'); + mDropBox.addText(TAG_NETSTATS_ERROR, builder.toString()); + } } } @@ -1278,7 +1328,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // trim any history beyond max if (mTime.hasCache()) { - final long currentTime = mTime.currentTimeMillis(); + final long currentTime = Math.min( + System.currentTimeMillis(), mTime.currentTimeMillis()); final long maxUidHistory = mSettings.getUidMaxHistory(); final long maxTagHistory = mSettings.getTagMaxHistory(); for (UidStatsKey key : mUidStats.keySet()) { @@ -1490,10 +1541,30 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Return the delta between two {@link NetworkStats} snapshots, where {@code * before} can be {@code null}. */ - private static NetworkStats computeStatsDelta( - NetworkStats before, NetworkStats current, boolean collectStale) { + private NetworkStats computeStatsDelta( + NetworkStats before, NetworkStats current, boolean collectStale, String type) { if (before != null) { - return current.subtractClamped(before); + try { + return current.subtract(before, false); + } catch (NonMonotonicException e) { + Log.w(TAG, "found non-monotonic values; saving to dropbox"); + + // record error for debugging + final StringBuilder builder = new StringBuilder(); + builder.append("found non-monotonic " + type + " values at left[" + e.leftIndex + + "] - right[" + e.rightIndex + "]\n"); + builder.append("left=").append(e.left).append('\n'); + builder.append("right=").append(e.right).append('\n'); + mDropBox.addText(TAG_NETSTATS_ERROR, builder.toString()); + + try { + // return clamped delta to help recover + return current.subtract(before, true); + } catch (NonMonotonicException e1) { + Log.wtf(TAG, "found non-monotonic values; returning empty delta", e1); + return new NetworkStats(0L, 10); + } + } } else if (collectStale) { // caller is okay collecting stale stats for first call. return current; @@ -1504,6 +1575,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + /** + * Return snapshot of current tethering statistics. Will return empty + * {@link NetworkStats} if any problems are encountered. + */ + private NetworkStats getNetworkStatsTethering() throws RemoteException { + try { + final String[] tetheredIfacePairs = mConnManager.getTetheredIfacePairs(); + return mNetworkManager.getNetworkStatsTethering(tetheredIfacePairs); + } catch (IllegalStateException e) { + Log.wtf(TAG, "problem reading network stats", e); + return new NetworkStats(0L, 10); + } + } + private static NetworkStats computeNetworkXtSnapshotFromUid(NetworkStats uidSnapshot) { return uidSnapshot.groupedByIface(); } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 0e9f64c..6b61c47 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -2162,6 +2162,9 @@ public class PackageManagerService extends IPackageManager.Stub { int flags, List<ResolveInfo> query, int priority) { // writer synchronized (mPackages) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + } if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); List<PreferredActivity> prefs = mSettings.mPreferredActivities.queryIntent(intent, resolvedType, @@ -2242,7 +2245,13 @@ public class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags) { - final ComponentName comp = intent.getComponent(); + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } if (comp != null) { final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); final ActivityInfo ai = getActivityInfo(comp, flags); @@ -2440,6 +2449,12 @@ public class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) { ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } if (comp != null) { List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); ActivityInfo ai = getReceiverInfo(comp, flags); @@ -2478,7 +2493,13 @@ public class PackageManagerService extends IPackageManager.Stub { } public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags) { - final ComponentName comp = intent.getComponent(); + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } if (comp != null) { final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); final ServiceInfo si = getServiceInfo(comp, flags); @@ -7947,7 +7968,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { boolean printedSomething = false; - for (PackageParser.Provider p : mProviders.values()) { + for (PackageParser.Provider p : mProvidersByComponent.values()) { if (packageName != null && !packageName.equals(p.info.packageName)) { continue; } @@ -7957,8 +7978,23 @@ public class PackageManagerService extends IPackageManager.Stub { pw.println("Registered ContentProviders:"); printedSomething = true; } - pw.print(" ["); pw.print(p.info.authority); pw.print("]: "); - pw.println(p.toString()); + pw.print(" "); pw.print(p.getComponentShortName()); pw.println(":"); + pw.print(" "); pw.println(p.toString()); + } + printedSomething = false; + for (Map.Entry<String, PackageParser.Provider> entry : mProviders.entrySet()) { + PackageParser.Provider p = entry.getValue(); + if (packageName != null && !packageName.equals(p.info.packageName)) { + continue; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(" "); + pw.println("ContentProvider Authorities:"); + printedSomething = true; + } + pw.print(" ["); pw.print(entry.getKey()); pw.println("]:"); + pw.print(" "); pw.println(p.toString()); } } diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index bfe6613..36442a0 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -63,6 +63,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import libcore.io.IoUtils; + /** * Holds information about dynamic settings. */ @@ -998,8 +1000,8 @@ final class Settings { FileUtils.sync(fstr); str.close(); journal.commit(); - } - catch (Exception e) { + } catch (Exception e) { + IoUtils.closeQuietly(str); journal.rollback(); } diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java index 61c96bb..0e3d20a 100644 --- a/services/java/com/android/server/wm/AppWindowToken.java +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -77,6 +77,9 @@ class AppWindowToken extends WindowToken { // Last visibility state we reported to the app token. boolean reportedVisible; + // Last drawn state we reported to the app token. + boolean reportedDrawn; + // Set to true when the token has been removed from the window mgr. boolean removed; @@ -277,6 +280,7 @@ class AppWindowToken extends WindowToken { int numInteresting = 0; int numVisible = 0; + int numDrawn = 0; boolean nowGone = true; if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Update reported visibility: " + this); @@ -307,6 +311,7 @@ class AppWindowToken extends WindowToken { } numInteresting++; if (win.isDrawnLw()) { + numDrawn++; if (!win.isAnimating()) { numVisible++; } @@ -316,9 +321,27 @@ class AppWindowToken extends WindowToken { } } + boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting; boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting; + if (!nowGone) { + // If the app is not yet gone, then it can only become visible/drawn. + if (!nowDrawn) { + nowDrawn = reportedDrawn; + } + if (!nowVisible) { + nowVisible = reportedVisible; + } + } if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "VIS " + this + ": interesting=" + numInteresting + " visible=" + numVisible); + if (nowDrawn != reportedDrawn) { + if (nowDrawn) { + Message m = service.mH.obtainMessage( + H.REPORT_APPLICATION_TOKEN_DRAWN, this); + service.mH.sendMessage(m); + } + reportedDrawn = nowDrawn; + } if (nowVisible != reportedVisible) { if (WindowManagerService.DEBUG_VISIBILITY) Slog.v( WindowManagerService.TAG, "Visibility changed in " + this @@ -360,6 +383,7 @@ class AppWindowToken extends WindowToken { pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested); pw.print(" clientHidden="); pw.print(clientHidden); pw.print(" willBeHidden="); pw.print(willBeHidden); + pw.print(" reportedDrawn="); pw.print(reportedDrawn); pw.print(" reportedVisible="); pw.println(reportedVisible); if (paused || freezingScreen) { pw.print(prefix); pw.print("paused="); pw.print(paused); diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index df7e0e1..a4f0a0c 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -675,7 +675,13 @@ public class InputManager implements Watchdog.Monitor { } catch (NumberFormatException e) { } if (result < 1) { - result = 55; + // This number equates to the refresh rate * 1.5. The rate should be at least + // equal to the screen refresh rate. We increase the rate by 50% to compensate for + // the discontinuity between the actual rate that events come in at (they do + // not necessarily come in constantly and are not handled synchronously). + // Ideally, we would use Display.getRefreshRate(), but as this does not necessarily + // return a sensible result, we use '60' as our default assumed refresh rate. + result = 90; } return result; } diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java index 131f11c..8fc9a70 100644 --- a/services/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java @@ -88,13 +88,14 @@ class ScreenRotationAnimation { try { try { mSurface = new Surface(session, 0, "FreezeSurface", - -1, mWidth, mHeight, PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT); + -1, mWidth, mHeight, PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN); if (mSurface == null || !mSurface.isValid()) { // Screenshot failed, punt. mSurface = null; return; } mSurface.setLayer(FREEZE_LAYER + 1); + mSurface.show(); } catch (Surface.OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate freeze surface", e); } diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java index 03b7546..77575f2 100644 --- a/services/java/com/android/server/wm/Session.java +++ b/services/java/com/android/server/wm/Session.java @@ -151,18 +151,22 @@ final class Session extends IWindowSession.Stub public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, - boolean insetsPending, Rect outFrame, Rect outContentInsets, + int flags, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); int res = mService.relayoutWindow(this, window, seq, attrs, - requestedWidth, requestedHeight, viewFlags, insetsPending, + requestedWidth, requestedHeight, viewFlags, flags, outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); return res; } + public void performDeferredDestroy(IWindow window) { + mService.performDeferredDestroyWindow(this, window); + } + public boolean outOfMemory(IWindow window) { return mService.outOfMemoryWindow(this, window); } @@ -306,7 +310,15 @@ final class Session extends IWindowSession.Stub synchronized (mService.mWindowMap) { long ident = Binder.clearCallingIdentity(); try { - if (mService.mDragState == null || mService.mDragState.mToken != token) { + if (mService.mDragState == null) { + // Most likely the drop recipient ANRed and we ended the drag + // out from under it. Log the issue and move on. + Slog.w(WindowManagerService.TAG, "Drop result given but no drag in progress"); + return; + } + + if (mService.mDragState.mToken != token) { + // We're in a drag, but the wrong window has responded. Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window); throw new IllegalStateException("reportDropResult() by non-recipient"); } diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 122d515..f5c2de9 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -429,6 +429,18 @@ public class WindowManagerService extends IWindowManager.Stub boolean mSystemBooted = false; boolean mForceDisplayEnabled = false; boolean mShowingBootMessages = false; + + // This protects the following display size properties, so that + // getDisplaySize() doesn't need to acquire the global lock. This is + // needed because the window manager sometimes needs to use ActivityThread + // while it has its global state locked (for example to load animation + // resources), but the ActivityThread also needs get the current display + // size sometimes when it has its package lock held. + // + // These will only be modified with both mWindowMap and mDisplaySizeLock + // held (in that order) so the window manager doesn't need to acquire this + // lock when needing these values in its normal operation. + final Object mDisplaySizeLock = new Object(); int mInitialDisplayWidth = 0; int mInitialDisplayHeight = 0; int mBaseDisplayWidth = 0; @@ -437,6 +449,7 @@ public class WindowManagerService extends IWindowManager.Stub int mCurDisplayHeight = 0; int mAppDisplayWidth = 0; int mAppDisplayHeight = 0; + int mRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean mAltOrientation = false; @@ -1836,7 +1849,8 @@ public class WindowManagerService extends IWindowManager.Stub rawChanged = true; } - if (rawChanged) { + if (rawChanged && (wallpaperWin.getAttrs().privateFlags & + WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { try { if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " + wallpaperWin + " x=" + wallpaperWin.mWallpaperX @@ -1886,12 +1900,10 @@ public class WindowManagerService extends IWindowManager.Stub } } - boolean updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { + void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { final int dw = mAppDisplayWidth; final int dh = mAppDisplayHeight; - boolean changed = false; - WindowState target = mWallpaperTarget; if (target != null) { if (target.mWallpaperX >= 0) { @@ -1916,14 +1928,31 @@ public class WindowManagerService extends IWindowManager.Stub WindowState wallpaper = token.windows.get(curWallpaperIndex); if (updateWallpaperOffsetLocked(wallpaper, dw, dh, sync)) { wallpaper.computeShownFrameLocked(); - changed = true; + // No need to lay out the windows - we can just set the wallpaper position + // directly. + if (wallpaper.mSurfaceX != wallpaper.mShownFrame.left + || wallpaper.mSurfaceY != wallpaper.mShownFrame.top) { + Surface.openTransaction(); + try { + if (SHOW_TRANSACTIONS) logSurface(wallpaper, + "POS " + wallpaper.mShownFrame.left + + ", " + wallpaper.mShownFrame.top, null); + wallpaper.mSurfaceX = wallpaper.mShownFrame.left; + wallpaper.mSurfaceY = wallpaper.mShownFrame.top; + wallpaper.mSurface.setPosition(wallpaper.mShownFrame.left, + wallpaper.mShownFrame.top); + } catch (RuntimeException e) { + Slog.w(TAG, "Error positioning surface of " + wallpaper + + " pos=(" + wallpaper.mShownFrame.left + + "," + wallpaper.mShownFrame.top + ")", e); + } + Surface.closeTransaction(); + } // We only want to be synchronous with one wallpaper. sync = false; } } } - - return changed; } void updateWallpaperVisibilityLocked() { @@ -2436,9 +2465,7 @@ public class WindowManagerService extends IWindowManager.Stub window.mWallpaperY = y; window.mWallpaperXStep = xStep; window.mWallpaperYStep = yStep; - if (updateWallpaperOffsetLocked(window, true)) { - performLayoutAndPlaceSurfacesLocked(); - } + updateWallpaperOffsetLocked(window, true); } } @@ -2485,12 +2512,13 @@ public class WindowManagerService extends IWindowManager.Stub public int relayoutWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int requestedWidth, - int requestedHeight, int viewVisibility, boolean insetsPending, + int requestedHeight, int viewVisibility, int flags, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { boolean displayed = false; boolean inTouchMode; boolean configChanged; + boolean surfaceChanged = false; // if they don't have this permission, mask out the status bar bits int systemUiVisibility = 0; @@ -2520,6 +2548,9 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.adjustWindowParamsLw(attrs); } + win.mSurfaceDestroyDeferred = + (flags&WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY) != 0; + int attrChanges = 0; int flagChanges = 0; if (attrs != null) { @@ -2578,11 +2609,7 @@ public class WindowManagerService extends IWindowManager.Stub (win.mAppToken == null || !win.mAppToken.clientHidden)) { displayed = !win.isVisibleLw(); if (win.mExiting) { - win.mExiting = false; - if (win.mAnimation != null) { - win.mAnimation.cancel(); - win.mAnimation = null; - } + win.cancelExitAnimationForNextAnimationLocked(); } if (win.mDestroying) { win.mDestroying = false; @@ -2620,8 +2647,12 @@ public class WindowManagerService extends IWindowManager.Stub // To change the format, we need to re-build the surface. win.destroySurfaceLocked(); displayed = true; + surfaceChanged = true; } try { + if (win.mSurface == null) { + surfaceChanged = true; + } Surface surface = win.createSurfaceLocked(); if (surface != null) { outSurface.copyFrom(surface); @@ -2673,6 +2704,7 @@ public class WindowManagerService extends IWindowManager.Stub // If we are not currently running the exit animation, we // need to see about starting one. if (!win.mExiting || win.mSurfacePendingDestroy) { + surfaceChanged = true; // Try starting an animation; if there isn't one, we // can destroy the surface right away. int transit = WindowManagerPolicy.TRANSIT_EXIT; @@ -2705,10 +2737,10 @@ public class WindowManagerService extends IWindowManager.Stub if (win.mSurface == null || (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0 || win.mSurfacePendingDestroy) { - // We are being called from a local process, which + // We could be called from a local process, which // means outSurface holds its current surface. Ensure the - // surface object is cleared, but we don't want it actually - // destroyed at this point. + // surface object is cleared, but we don't necessarily want + // it actually destroyed at this point. win.mSurfacePendingDestroy = false; outSurface.release(); if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win); @@ -2750,7 +2782,7 @@ public class WindowManagerService extends IWindowManager.Stub } mLayoutNeeded = true; - win.mGivenInsetsPending = insetsPending; + win.mGivenInsetsPending = (flags&WindowManagerImpl.RELAYOUT_INSETS_PENDING) != 0; if (assignLayers) { assignLayersLocked(); } @@ -2787,8 +2819,25 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); - return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) - | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0); + return (inTouchMode ? WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE : 0) + | (displayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0) + | (surfaceChanged ? WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED : 0); + } + + public void performDeferredDestroyWindow(Session session, IWindow client) { + long origId = Binder.clearCallingIdentity(); + + try { + synchronized(mWindowMap) { + WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return; + } + win.destroyDeferredSurfaceLocked(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } } public boolean outOfMemoryWindow(Session session, IWindow client) { @@ -3060,7 +3109,7 @@ public class WindowManagerService extends IWindowManager.Stub // Application Window Tokens // ------------------------------------------------------------- - public void validateAppTokens(List tokens) { + public void validateAppTokens(List<IBinder> tokens) { int v = tokens.size()-1; int m = mAppTokens.size()-1; while (v >= 0 && m >= 0) { @@ -3728,7 +3777,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } - // If this is a translucent or wallpaper window, then don't + // If this is a translucent window, then don't // show a starting window -- the current effect (a full-screen // opaque starting window that fades away to the real contents // when it is ready) does not work for this. @@ -3745,7 +3794,16 @@ public class WindowManagerService extends IWindowManager.Stub } if (ent.array.getBoolean( com.android.internal.R.styleable.Window_windowShowWallpaper, false)) { - return; + if (mWallpaperTarget == null) { + // If this theme is requesting a wallpaper, and the wallpaper + // is not curently visible, then this effectively serves as + // an opaque window and our starting window transition animation + // can still work. We just need to make sure the starting window + // is also showing the wallpaper. + windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; + } else { + return; + } } } @@ -5080,10 +5138,22 @@ public class WindowManagerService extends IWindowManager.Stub // Constrain thumbnail to smaller of screen width or height. Assumes aspect // of thumbnail is the same as the screen (in landscape) or square. + float targetWidthScale = width / (float) fw; + float targetHeightScale = height / (float) fh; if (dw <= dh) { - scale = width / (float) fw; // portrait + scale = targetWidthScale; + // If aspect of thumbnail is the same as the screen (in landscape), + // select the slightly larger value so we fill the entire bitmap + if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) { + scale = targetHeightScale; + } } else { - scale = height / (float) fh; // landscape + scale = targetHeightScale; + // If aspect of thumbnail is the same as the screen (in landscape), + // select the slightly larger value so we fill the entire bitmap + if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) { + scale = targetWidthScale; + } } // The screen shot will contain the entire screen. @@ -5799,7 +5869,87 @@ public class WindowManagerService extends IWindowManager.Stub return curSize; } - private int computeSmallestWidth(boolean rotated, int dw, int dh, float density) { + private int reduceConfigLayout(int curLayout, int rotation, float density, + int dw, int dh) { + // Get the app screen size at this rotation. + int w = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation); + int h = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation); + + // Compute the screen layout size class for this rotation. + int screenLayoutSize; + boolean screenLayoutLong; + boolean screenLayoutCompatNeeded; + int longSize = w; + int shortSize = h; + if (longSize < shortSize) { + int tmp = longSize; + longSize = shortSize; + shortSize = tmp; + } + longSize = (int)(longSize/density); + shortSize = (int)(shortSize/density); + + // These semi-magic numbers define our compatibility modes for + // applications with different screens. These are guarantees to + // app developers about the space they can expect for a particular + // configuration. DO NOT CHANGE! + if (longSize < 470) { + // This is shorter than an HVGA normal density screen (which + // is 480 pixels on its long side). + screenLayoutSize = Configuration.SCREENLAYOUT_SIZE_SMALL; + screenLayoutLong = false; + screenLayoutCompatNeeded = false; + } else { + // What size is this screen screen? + if (longSize >= 960 && shortSize >= 720) { + // 1.5xVGA or larger screens at medium density are the point + // at which we consider it to be an extra large screen. + screenLayoutSize = Configuration.SCREENLAYOUT_SIZE_XLARGE; + } else if (longSize >= 640 && shortSize >= 480) { + // VGA or larger screens at medium density are the point + // at which we consider it to be a large screen. + screenLayoutSize = Configuration.SCREENLAYOUT_SIZE_LARGE; + } else { + screenLayoutSize = Configuration.SCREENLAYOUT_SIZE_NORMAL; + } + + // If this screen is wider than normal HVGA, or taller + // than FWVGA, then for old apps we want to run in size + // compatibility mode. + if (shortSize > 321 || longSize > 570) { + screenLayoutCompatNeeded = true; + } else { + screenLayoutCompatNeeded = false; + } + + // Is this a long screen? + if (((longSize*3)/5) >= (shortSize-1)) { + // Anything wider than WVGA (5:3) is considering to be long. + screenLayoutLong = true; + } else { + screenLayoutLong = false; + } + } + + // Now reduce the last screenLayout to not be better than what we + // have found. + if (!screenLayoutLong) { + curLayout = (curLayout&~Configuration.SCREENLAYOUT_LONG_MASK) + | Configuration.SCREENLAYOUT_LONG_NO; + } + if (screenLayoutCompatNeeded) { + curLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED; + } + int curSize = curLayout&Configuration.SCREENLAYOUT_SIZE_MASK; + if (screenLayoutSize < curSize) { + curLayout = (curLayout&~Configuration.SCREENLAYOUT_SIZE_MASK) + | screenLayoutSize; + } + return curLayout; + } + + private void computeSmallestWidthAndScreenLayout(boolean rotated, int dw, int dh, + float density, Configuration outConfig) { // We need to determine the smallest width that will occur under normal // operation. To this, start with the base screen size and compute the // width under the different possible rotations. We need to un-rotate @@ -5816,7 +5966,14 @@ public class WindowManagerService extends IWindowManager.Stub sw = reduceConfigWidthSize(sw, Surface.ROTATION_90, density, unrotDh, unrotDw); sw = reduceConfigWidthSize(sw, Surface.ROTATION_180, density, unrotDw, unrotDh); sw = reduceConfigWidthSize(sw, Surface.ROTATION_270, density, unrotDh, unrotDw); - return sw; + int sl = Configuration.SCREENLAYOUT_SIZE_XLARGE + | Configuration.SCREENLAYOUT_LONG_YES; + sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh); + sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw); + sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh); + sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw); + outConfig.smallestScreenWidthDp = sw; + outConfig.screenLayout = sl; } private int reduceCompatConfigWidthSize(int curSize, int rotation, DisplayMetrics dm, @@ -5862,25 +6019,27 @@ public class WindowManagerService extends IWindowManager.Stub final int realdw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth; final int realdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; - if (mAltOrientation) { - mCurDisplayWidth = realdw; - mCurDisplayHeight = realdh; - if (realdw > realdh) { - // Turn landscape into portrait. - int maxw = (int)(realdh/1.3f); - if (maxw < realdw) { - mCurDisplayWidth = maxw; + synchronized(mDisplaySizeLock) { + if (mAltOrientation) { + mCurDisplayWidth = realdw; + mCurDisplayHeight = realdh; + if (realdw > realdh) { + // Turn landscape into portrait. + int maxw = (int)(realdh/1.3f); + if (maxw < realdw) { + mCurDisplayWidth = maxw; + } + } else { + // Turn portrait into landscape. + int maxh = (int)(realdw/1.3f); + if (maxh < realdh) { + mCurDisplayHeight = maxh; + } } } else { - // Turn portrait into landscape. - int maxh = (int)(realdw/1.3f); - if (maxh < realdh) { - mCurDisplayHeight = maxh; - } + mCurDisplayWidth = realdw; + mCurDisplayHeight = realdh; } - } else { - mCurDisplayWidth = realdw; - mCurDisplayHeight = realdh; } final int dw = mCurDisplayWidth; @@ -5899,8 +6058,12 @@ public class WindowManagerService extends IWindowManager.Stub // Update application display metrics. final DisplayMetrics dm = mDisplayMetrics; - mAppDisplayWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation); - mAppDisplayHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation); + final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation); + final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation); + synchronized(mDisplaySizeLock) { + mAppDisplayWidth = appWidth; + mAppDisplayHeight = appHeight; + } if (false) { Slog.i(TAG, "Set app display size: " + mAppDisplayWidth + " x " + mAppDisplayHeight); @@ -5914,64 +6077,12 @@ public class WindowManagerService extends IWindowManager.Stub / dm.density); config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation) / dm.density); - config.smallestScreenWidthDp = computeSmallestWidth(rotated, dw, dh, dm.density); + computeSmallestWidthAndScreenLayout(rotated, dw, dh, dm.density, config); config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale); config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale); config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh); - // Compute the screen layout size class. - int screenLayout; - int longSize = mAppDisplayWidth; - int shortSize = mAppDisplayHeight; - if (longSize < shortSize) { - int tmp = longSize; - longSize = shortSize; - shortSize = tmp; - } - longSize = (int)(longSize/dm.density); - shortSize = (int)(shortSize/dm.density); - - // These semi-magic numbers define our compatibility modes for - // applications with different screens. These are guarantees to - // app developers about the space they can expect for a particular - // configuration. DO NOT CHANGE! - if (longSize < 470) { - // This is shorter than an HVGA normal density screen (which - // is 480 pixels on its long side). - screenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL - | Configuration.SCREENLAYOUT_LONG_NO; - } else { - // What size is this screen screen? - if (longSize >= 960 && shortSize >= 720) { - // 1.5xVGA or larger screens at medium density are the point - // at which we consider it to be an extra large screen. - screenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE; - } else if (longSize >= 640 && shortSize >= 480) { - // VGA or larger screens at medium density are the point - // at which we consider it to be a large screen. - screenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE; - } else { - screenLayout = Configuration.SCREENLAYOUT_SIZE_NORMAL; - } - - // If this screen is wider than normal HVGA, or taller - // than FWVGA, then for old apps we want to run in size - // compatibility mode. - if (shortSize > 321 || longSize > 570) { - screenLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED; - } - - // Is this a long screen? - if (((longSize*3)/5) >= (shortSize-1)) { - // Anything wider than WVGA (5:3) is considering to be long. - screenLayout |= Configuration.SCREENLAYOUT_LONG_YES; - } else { - screenLayout |= Configuration.SCREENLAYOUT_LONG_NO; - } - } - config.screenLayout = screenLayout; - // Determine whether a hard keyboard is available and enabled. boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS; if (hardKeyboardAvailable != mHardKeyboardAvailable) { @@ -6322,18 +6433,20 @@ public class WindowManagerService extends IWindowManager.Stub } WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); mDisplay = wm.getDefaultDisplay(); - mInitialDisplayWidth = mDisplay.getRawWidth(); - mInitialDisplayHeight = mDisplay.getRawHeight(); - int rot = mDisplay.getRotation(); - if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { - // If the screen is currently rotated, we need to swap the - // initial width and height to get the true natural values. - int tmp = mInitialDisplayWidth; - mInitialDisplayWidth = mInitialDisplayHeight; - mInitialDisplayHeight = tmp; - } - mBaseDisplayWidth = mCurDisplayWidth = mAppDisplayWidth = mInitialDisplayWidth; - mBaseDisplayHeight = mCurDisplayHeight = mAppDisplayHeight = mInitialDisplayHeight; + synchronized(mDisplaySizeLock) { + mInitialDisplayWidth = mDisplay.getRawWidth(); + mInitialDisplayHeight = mDisplay.getRawHeight(); + int rot = mDisplay.getRotation(); + if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { + // If the screen is currently rotated, we need to swap the + // initial width and height to get the true natural values. + int tmp = mInitialDisplayWidth; + mInitialDisplayWidth = mInitialDisplayHeight; + mInitialDisplayHeight = tmp; + } + mBaseDisplayWidth = mCurDisplayWidth = mAppDisplayWidth = mInitialDisplayWidth; + mBaseDisplayHeight = mCurDisplayHeight = mAppDisplayHeight = mInitialDisplayHeight; + } mInputManager.setDisplaySize(Display.DEFAULT_DISPLAY, mDisplay.getRawWidth(), mDisplay.getRawHeight(), mDisplay.getRawExternalWidth(), mDisplay.getRawExternalHeight()); @@ -6376,6 +6489,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int REMOVE_STARTING = 6; public static final int FINISHED_STARTING = 7; public static final int REPORT_APPLICATION_TOKEN_WINDOWS = 8; + public static final int REPORT_APPLICATION_TOKEN_DRAWN = 9; public static final int WINDOW_FREEZE_TIMEOUT = 11; public static final int HOLD_SCREEN_CHANGED = 12; public static final int APP_TRANSITION_TIMEOUT = 13; @@ -6589,6 +6703,17 @@ public class WindowManagerService extends IWindowManager.Stub } } break; + case REPORT_APPLICATION_TOKEN_DRAWN: { + final AppWindowToken wtoken = (AppWindowToken)msg.obj; + + try { + if (DEBUG_VISIBILITY) Slog.v( + TAG, "Reporting drawn in " + wtoken); + wtoken.appToken.windowsDrawn(); + } catch (RemoteException ex) { + } + } break; + case REPORT_APPLICATION_TOKEN_WINDOWS: { final AppWindowToken wtoken = (AppWindowToken)msg.obj; @@ -6859,28 +6984,28 @@ public class WindowManagerService extends IWindowManager.Stub } public void getDisplaySize(Point size) { - synchronized(mWindowMap) { + synchronized(mDisplaySizeLock) { size.x = mAppDisplayWidth; size.y = mAppDisplayHeight; } } public void getRealDisplaySize(Point size) { - synchronized(mWindowMap) { + synchronized(mDisplaySizeLock) { size.x = mCurDisplayWidth; size.y = mCurDisplayHeight; } } public void getInitialDisplaySize(Point size) { - synchronized(mWindowMap) { + synchronized(mDisplaySizeLock) { size.x = mInitialDisplayWidth; size.y = mInitialDisplayHeight; } } public int getMaximumSizeDimension() { - synchronized(mWindowMap) { + synchronized(mDisplaySizeLock) { // Do this based on the raw screen size, until we are smarter. return mBaseDisplayWidth > mBaseDisplayHeight ? mBaseDisplayWidth : mBaseDisplayHeight; @@ -6973,8 +7098,10 @@ public class WindowManagerService extends IWindowManager.Stub private void setForcedDisplaySizeLocked(int width, int height) { Slog.i(TAG, "Using new display size: " + width + "x" + height); - mBaseDisplayWidth = width; - mBaseDisplayHeight = height; + synchronized(mDisplaySizeLock) { + mBaseDisplayWidth = width; + mBaseDisplayHeight = height; + } mPolicy.setInitialDisplaySize(mBaseDisplayWidth, mBaseDisplayHeight); mLayoutNeeded = true; @@ -7590,7 +7717,8 @@ public class WindowManagerService extends IWindowManager.Stub // a detached wallpaper animation. if (nowAnimating) { if (w.mAnimation != null) { - if (w.mAnimation.getDetachWallpaper()) { + if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 + && w.mAnimation.getDetachWallpaper()) { windowDetachedWallpaper = w; } if (w.mAnimation.getBackgroundColor() != 0) { @@ -7610,7 +7738,8 @@ public class WindowManagerService extends IWindowManager.Stub // displayed behind it. if (w.mAppToken != null && w.mAppToken.animation != null && w.mAppToken.animating) { - if (w.mAppToken.animation.getDetachWallpaper()) { + if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 + && w.mAppToken.animation.getDetachWallpaper()) { windowDetachedWallpaper = w; } if (w.mAppToken.animation.getBackgroundColor() != 0) { diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index e9875a5..aa7bf2d 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -85,6 +85,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { boolean mPolicyVisibilityAfterAnim = true; boolean mAppFreezing; Surface mSurface; + Surface mPendingDestroySurface; boolean mReportDestroySurface; boolean mSurfacePendingDestroy; boolean mAttachedHidden; // is our parent window hidden? @@ -121,7 +122,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { * we must tell them application to resize (and thus redraw itself). */ boolean mSurfaceResized; - + + /** + * Set if the client has asked that the destroy of its surface be delayed + * until it explicitly says it is okay. + */ + boolean mSurfaceDestroyDeferred; + /** * Insets that determine the actually visible area. These are in the application's * coordinate space (without compatibility scale applied). @@ -596,11 +603,24 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + // TODO: Fix and call finishExit() instead of cancelExitAnimationForNextAnimationLocked() + // for avoiding the code duplication. + void cancelExitAnimationForNextAnimationLocked() { + if (!mExiting) return; + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + destroySurfaceLocked(); + } + mExiting = false; + } + Surface createSurfaceLocked() { if (mSurface == null) { mReportDestroySurface = false; mSurfacePendingDestroy = false; - Slog.i(WindowManagerService.TAG, "createSurface " + this + ": DRAW NOW PENDING"); + if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(WindowManagerService.TAG, + "createSurface " + this + ": DRAW NOW PENDING"); mDrawPending = true; mCommitDrawPending = false; mReadyToShow = false; @@ -751,15 +771,32 @@ final class WindowState implements WindowManagerPolicy.WindowState { Slog.w(WindowManagerService.TAG, "Window " + this + " destroying surface " + mSurface + ", session " + mSession, e); } - if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { - RuntimeException e = null; - if (!WindowManagerService.HIDE_STACK_CRAWLS) { - e = new RuntimeException(); - e.fillInStackTrace(); + if (mSurfaceDestroyDeferred) { + if (mSurface != null && mPendingDestroySurface != mSurface) { + if (mPendingDestroySurface != null) { + if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + WindowManagerService.logSurface(this, "DESTROY PENDING", e); + } + mPendingDestroySurface.destroy(); + } + mPendingDestroySurface = mSurface; } - WindowManagerService.logSurface(this, "DESTROY", e); + } else { + if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + WindowManagerService.logSurface(this, "DESTROY", e); + } + mSurface.destroy(); } - mSurface.destroy(); } catch (RuntimeException e) { Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window " + this + " surface " + mSurface + " session " + mSession @@ -771,6 +808,28 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + void destroyDeferredSurfaceLocked() { + try { + if (mPendingDestroySurface != null) { + if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { + RuntimeException e = null; + if (!WindowManagerService.HIDE_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + mService.logSurface(this, "DESTROY PENDING", e); + } + mPendingDestroySurface.destroy(); + } + } catch (RuntimeException e) { + Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window " + + this + " surface " + mPendingDestroySurface + + " session " + mSession + ": " + e.toString()); + } + mSurfaceDestroyDeferred = false; + mPendingDestroySurface = null; + } + boolean finishDrawingLocked() { if (mDrawPending) { if (SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) Slog.v( @@ -964,6 +1023,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { mAnimation.cancel(); mAnimation = null; } + if (mService.mWindowDetachedWallpaper == this) { + mService.mWindowDetachedWallpaper = null; + } mAnimLayer = mLayer; if (mIsImWindow) { mAnimLayer += mService.mInputMethodAnimLayerAdjustment; @@ -1402,6 +1464,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Removing " + this + " from " + mAttachedWindow); mAttachedWindow.mChildWindows.remove(this); } + destroyDeferredSurfaceLocked(); destroySurfaceLocked(); mSession.windowRemovedLocked(); try { @@ -1599,6 +1662,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { pw.print(") "); pw.print(mSurfaceW); pw.print(" x "); pw.println(mSurfaceH); } + if (mPendingDestroySurface != null) { + pw.print(prefix); pw.print("mPendingDestroySurface="); + pw.println(mPendingDestroySurface); + } if (dumpAll) { pw.print(prefix); pw.print("mToken="); pw.println(mToken); pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken); @@ -1627,6 +1694,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (!mRelayoutCalled) { pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled); } + if (mSurfaceResized || mSurfaceDestroyDeferred) { + pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized); + pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred); + } if (mXOffset != 0 || mYOffset != 0) { pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); pw.print(" y="); pw.println(mYOffset); @@ -1745,4 +1816,4 @@ final class WindowState implements WindowManagerPolicy.WindowState { } return mStringNameCache; } -}
\ No newline at end of file +} diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 7e9fba8..f259883 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -98,6 +98,7 @@ static struct { jfieldID mName; jfieldID mSources; jfieldID mKeyboardType; + jfieldID mKeyCharacterMapFile; } gInputDeviceClassInfo; static struct { @@ -1231,10 +1232,16 @@ static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env, return NULL; } + jstring fileStr = env->NewStringUTF(deviceInfo.getKeyCharacterMapFile()); + if (!fileStr) { + return NULL; + } + env->SetIntField(deviceObj, gInputDeviceClassInfo.mId, deviceInfo.getId()); env->SetObjectField(deviceObj, gInputDeviceClassInfo.mName, deviceNameObj); env->SetIntField(deviceObj, gInputDeviceClassInfo.mSources, deviceInfo.getSources()); env->SetIntField(deviceObj, gInputDeviceClassInfo.mKeyboardType, deviceInfo.getKeyboardType()); + env->SetObjectField(deviceObj, gInputDeviceClassInfo.mKeyCharacterMapFile, fileStr); const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges(); for (size_t i = 0; i < ranges.size(); i++) { @@ -1516,6 +1523,9 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputDeviceClassInfo.mKeyboardType, gInputDeviceClassInfo.clazz, "mKeyboardType", "I"); + GET_FIELD_ID(gInputDeviceClassInfo.mKeyCharacterMapFile, gInputDeviceClassInfo.clazz, + "mKeyCharacterMapFile", "Ljava/lang/String;"); + // Configuration FIND_CLASS(clazz, "android/content/res/Configuration"); diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index d82a7e2..7575ebd 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -166,7 +166,11 @@ status_t SensorDevice::initCheck() const { ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { if (!mSensorDevice) return NO_INIT; - return mSensorDevice->poll(mSensorDevice, buffer, count); + ssize_t c; + do { + c = mSensorDevice->poll(mSensorDevice, buffer, count); + } while (c == -EINTR); + return c; } status_t SensorDevice::activate(void* ident, int handle, int enabled) diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 5b74fb8..6202143 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -286,7 +286,8 @@ bool SensorService::threadLoop() } } while (count >= 0 || Thread::exitPending()); - LOGW("Exiting SensorService::threadLoop!"); + LOGW("Exiting SensorService::threadLoop => aborting..."); + abort(); return false; } @@ -471,14 +472,21 @@ status_t SensorService::setEventRate(const sp<SensorEventConnection>& connection if (mInitCheck != NO_ERROR) return mInitCheck; + SensorInterface* sensor = mSensorMap.valueFor(handle); + if (!sensor) + return BAD_VALUE; + if (ns < 0) return BAD_VALUE; + nsecs_t minDelayNs = sensor->getSensor().getMinDelayNs(); + if (ns < minDelayNs) { + ns = minDelayNs; + } + if (ns < MINIMUM_EVENTS_PERIOD) ns = MINIMUM_EVENTS_PERIOD; - SensorInterface* sensor = mSensorMap.valueFor(handle); - if (!sensor) return BAD_VALUE; return sensor->setDelay(connection.get(), handle, ns); } diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 53502db..f63c0c1 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -28,10 +28,7 @@ ifeq ($(TARGET_BOARD_PLATFORM), omap4) endif ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE -endif - -ifneq (,$(findstring $(TARGET_DEVICE),tuna toro maguro)) - LOCAL_CFLAGS += -DREFRESH_RATE=59 + LOCAL_CFLAGS += -DREFRESH_RATE=56 endif diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 329c052..f94d321 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -284,22 +284,6 @@ void DisplayHardware::init(uint32_t dpy) glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); - -#ifdef EGL_ANDROID_swap_rectangle - if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) { - if (eglSetSwapRectangleANDROID(display, surface, - 0, 0, mWidth, mHeight) == EGL_TRUE) { - // This could fail if this extension is not supported by this - // specific surface (of config) - mFlags |= SWAP_RECTANGLE; - } - } - // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE - // choose PARTIAL_UPDATES, which should be more efficient - if (mFlags & PARTIAL_UPDATES) - mFlags &= ~SWAP_RECTANGLE; -#endif - LOGI("EGL informations:"); LOGI("# of configs : %d", numConfigs); LOGI("vendor : %s", extensions.getEglVendor()); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 50b8604..d3b0dbf 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -360,18 +360,6 @@ uint32_t Layer::doTransaction(uint32_t flags) mCurrentScalingMode); if (!isFixedSize()) { - // we're being resized and there is a freeze display request, - // acquire a freeze lock, so that the screen stays put - // until we've redrawn at the new size; this is to avoid - // glitches upon orientation changes. - if (mFlinger->hasFreezeRequest()) { - // if the surface is hidden, don't try to acquire the - // freeze lock, since hidden surfaces may never redraw - if (!(front.flags & ISurfaceComposer::eLayerHidden)) { - mFreezeLock = mFlinger->getFreezeLock(); - } - } - // this will make sure LayerBase::doTransaction doesn't update // the drawing state's size Layer::State& editDraw(mDrawingState); @@ -385,14 +373,6 @@ uint32_t Layer::doTransaction(uint32_t flags) temp.requested_h); } - if (temp.sequence != front.sequence) { - if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) { - // this surface is now hidden, so it shouldn't hold a freeze lock - // (it may never redraw, which is fine if it is hidden) - mFreezeLock.clear(); - } - } - return LayerBase::doTransaction(flags); } @@ -466,7 +446,7 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // update the layer size and release freeze-lock + // update the layer size if needed const Layer::State& front(drawingState()); // FIXME: mPostedDirtyRegion = dirty & bounds @@ -503,9 +483,6 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // recompute visible region recomputeVisibleRegions = true; - - // we now have the correct size, unfreeze the screen - mFreezeLock.clear(); } LOGD_IF(DEBUG_RESIZE, @@ -538,11 +515,6 @@ void Layer::unlockPageFlip( dirtyRegion.andSelf(visibleRegionScreen); outDirtyRegion.orSelf(dirtyRegion); } - if (visibleRegionScreen.isEmpty()) { - // an invisible layer should not hold a freeze-lock - // (because it may never be updated and therefore never release it) - mFreezeLock.clear(); - } } void Layer::dump(String8& result, char* buffer, size_t SIZE) const @@ -560,9 +532,9 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const snprintf(buffer, SIZE, " " "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X]," - " freezeLock=%p, transform-hint=0x%02x, queued-frames=%d\n", + " transform-hint=0x%02x, queued-frames=%d\n", mFormat, w0, h0, s0,f0, - getFreezeLock().get(), getTransformHint(), mQueuedFrames); + getTransformHint(), mQueuedFrames); result.append(buffer); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 82e3521..2b9471b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -39,7 +39,6 @@ namespace android { // --------------------------------------------------------------------------- -class FreezeLock; class Client; class GLExtensions; @@ -80,7 +79,6 @@ public: virtual wp<IBinder> getSurfaceTextureBinder() const; // only for debugging - inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; } protected: @@ -124,9 +122,6 @@ private: bool mProtectedByApp; // application requires protected path to external sink Region mPostedDirtyRegion; - // page-flip thread and transaction thread (currently main thread) - sp<FreezeLock> mFreezeLock; - // binder thread, transaction thread mutable Mutex mLock; }; diff --git a/services/surfaceflinger/LayerScreenshot.cpp b/services/surfaceflinger/LayerScreenshot.cpp index e30ccbf..68e6660 100644 --- a/services/surfaceflinger/LayerScreenshot.cpp +++ b/services/surfaceflinger/LayerScreenshot.cpp @@ -27,6 +27,7 @@ #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" + namespace android { // --------------------------------------------------------------------------- @@ -45,23 +46,64 @@ LayerScreenshot::~LayerScreenshot() } } +status_t LayerScreenshot::captureLocked() { + GLfloat u, v; + status_t result = mFlinger->renderScreenToTextureLocked(0, &mTextureName, &u, &v); + if (result != NO_ERROR) { + return result; + } + initTexture(u, v); + return NO_ERROR; +} + status_t LayerScreenshot::capture() { GLfloat u, v; status_t result = mFlinger->renderScreenToTexture(0, &mTextureName, &u, &v); if (result != NO_ERROR) { return result; } + initTexture(u, v); + return NO_ERROR; +} +void LayerScreenshot::initTexture(GLfloat u, GLfloat v) { glBindTexture(GL_TEXTURE_2D, mTextureName); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - mTexCoords[0] = 0; mTexCoords[1] = v; mTexCoords[2] = 0; mTexCoords[3] = 0; mTexCoords[4] = u; mTexCoords[5] = 0; mTexCoords[6] = u; mTexCoords[7] = v; +} - return NO_ERROR; +void LayerScreenshot::initStates(uint32_t w, uint32_t h, uint32_t flags) { + LayerBaseClient::initStates(w, h, flags); + if (!(flags & ISurfaceComposer::eHidden)) { + capture(); + } +} + +uint32_t LayerScreenshot::doTransaction(uint32_t flags) +{ + const Layer::State& draw(drawingState()); + const Layer::State& curr(currentState()); + + if (draw.flags & ISurfaceComposer::eLayerHidden) { + if (!(curr.flags & ISurfaceComposer::eLayerHidden)) { + // we're going from hidden to visible + status_t err = captureLocked(); + if (err != NO_ERROR) { + LOGW("createScreenshotSurface failed (%s)", strerror(-err)); + } + } + } else if (curr.flags & ISurfaceComposer::eLayerHidden) { + // we're going from visible to hidden + if (mTextureName) { + glDeleteTextures(1, &mTextureName); + mTextureName = 0; + } + } + return LayerBaseClient::doTransaction(flags); } void LayerScreenshot::onDraw(const Region& clip) const diff --git a/services/surfaceflinger/LayerScreenshot.h b/services/surfaceflinger/LayerScreenshot.h index e3a2b19..ab90047 100644 --- a/services/surfaceflinger/LayerScreenshot.h +++ b/services/surfaceflinger/LayerScreenshot.h @@ -41,12 +41,18 @@ public: status_t capture(); + virtual void initStates(uint32_t w, uint32_t h, uint32_t flags); + virtual uint32_t doTransaction(uint32_t flags); virtual void onDraw(const Region& clip) const; virtual bool isOpaque() const { return false; } virtual bool isSecure() const { return false; } virtual bool isProtectedByApp() const { return false; } virtual bool isProtectedByDRM() const { return false; } virtual const char* getTypeId() const { return "LayerScreenshot"; } + +private: + status_t captureLocked(); + void initTexture(GLfloat u, GLfloat v); }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ba8f630..24bd2a6 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -65,6 +65,8 @@ #define AID_GRAPHICS 1003 #endif +#define EGL_VERSION_HW_ANDROID 0x3143 + #define DISPLAY_COUNT 1 namespace android { @@ -80,15 +82,12 @@ const String16 sDump("android.permission.DUMP"); SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), Thread(false), mTransactionFlags(0), - mResizeTransationPending(false), + mTransationPending(false), mLayersRemoved(false), mBootTime(systemTime()), mVisibleRegionsDirty(false), mHwWorkListDirty(false), - mFreezeDisplay(false), mElectronBeamAnimationMode(0), - mFreezeCount(0), - mFreezeDisplayTime(0), mDebugRegion(0), mDebugBackground(0), mDebugDDMS(0), @@ -191,11 +190,6 @@ void SurfaceFlinger::binderDied(const wp<IBinder>& who) { // the window manager died on us. prepare its eulogy. - // unfreeze the screen in case it was... frozen - mFreezeDisplayTime = 0; - mFreezeCount = 0; - mFreezeDisplay = false; - // reset screen orientation setOrientation(0, eOrientationDefault, 0); @@ -323,33 +317,7 @@ void SurfaceFlinger::waitForEvent() { while (true) { nsecs_t timeout = -1; - const nsecs_t freezeDisplayTimeout = ms2ns(5000); - if (UNLIKELY(isFrozen())) { - // wait 5 seconds - const nsecs_t now = systemTime(); - if (mFreezeDisplayTime == 0) { - mFreezeDisplayTime = now; - } - nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime); - timeout = waitTime>0 ? waitTime : 0; - } - sp<MessageBase> msg = mEventQueue.waitMessage(timeout); - - // see if we timed out - if (isFrozen()) { - const nsecs_t now = systemTime(); - nsecs_t frozenTime = (now - mFreezeDisplayTime); - if (frozenTime >= freezeDisplayTimeout) { - // we timed out and are still frozen - LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d", - mFreezeDisplay, mFreezeCount); - mFreezeDisplayTime = 0; - mFreezeCount = 0; - mFreezeDisplay = false; - } - } - if (msg != 0) { switch (msg->what) { case MessageQueue::INVALIDATE: @@ -589,13 +557,6 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) mDirtyRegion.set(hw.bounds()); } - if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) { - // freezing or unfreezing the display -> trigger animation if needed - mFreezeDisplay = mCurrentState.freezeDisplay; - if (mFreezeDisplay) - mFreezeDisplayTime = 0; - } - if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { // layers have been added mVisibleRegionsDirty = true; @@ -621,11 +582,6 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) commitTransaction(); } -sp<FreezeLock> SurfaceFlinger::getFreezeLock() const -{ - return new FreezeLock(const_cast<SurfaceFlinger *>(this)); -} - void SurfaceFlinger::computeVisibleRegions( const LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) { @@ -754,8 +710,16 @@ void SurfaceFlinger::computeVisibleRegions( void SurfaceFlinger::commitTransaction() { + if (!mLayersPendingRemoval.isEmpty()) { + // Notify removed layers now that they can't be drawn from + for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) { + mLayersPendingRemoval[i]->onRemoved(); + } + mLayersPendingRemoval.clear(); + } + mDrawingState = mCurrentState; - mResizeTransationPending = false; + mTransationPending = false; mTransactionCV.broadcast(); } @@ -1206,7 +1170,7 @@ status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase) mLayerPurgatory.add(layerBase); } - layerBase->onRemoved(); + mLayersPendingRemoval.push(layerBase); // it's possible that we don't find a layer, because it might // have been destroyed already -- this is not technically an error @@ -1243,15 +1207,14 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state, - int orientation) { + int orientation, uint32_t flags) { Mutex::Autolock _l(mStateLock); - uint32_t flags = 0; + uint32_t transactionFlags = 0; if (mCurrentState.orientation != orientation) { if (uint32_t(orientation)<=eOrientation270 || orientation==42) { mCurrentState.orientation = orientation; - flags |= eTransactionNeeded; - mResizeTransationPending = true; + transactionFlags |= eTransactionNeeded; } else if (orientation != eOrientationUnchanged) { LOGW("setTransactionState: ignoring unrecognized orientation: %d", orientation); @@ -1262,56 +1225,31 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state, for (size_t i=0 ; i<count ; i++) { const ComposerState& s(state[i]); sp<Client> client( static_cast<Client *>(s.client.get()) ); - flags |= setClientStateLocked(client, s.state); - } - if (flags) { - setTransactionFlags(flags); - } - - signalEvent(); - - // if there is a transaction with a resize, wait for it to - // take effect before returning. - while (mResizeTransationPending) { - status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); - if (CC_UNLIKELY(err != NO_ERROR)) { - // just in case something goes wrong in SF, return to the - // called after a few seconds. - LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); - mResizeTransationPending = false; - break; + transactionFlags |= setClientStateLocked(client, s.state); + } + + if (transactionFlags) { + // this triggers the transaction + setTransactionFlags(transactionFlags); + + // if this is a synchronous transaction, wait for it to take effect + // before returning. + if (flags & eSynchronous) { + mTransationPending = true; + } + while (mTransationPending) { + status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + // just in case something goes wrong in SF, return to the + // called after a few seconds. + LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); + mTransationPending = false; + break; + } } } } -status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags) -{ - if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) - return BAD_VALUE; - - Mutex::Autolock _l(mStateLock); - mCurrentState.freezeDisplay = 1; - setTransactionFlags(eTransactionNeeded); - - // flags is intended to communicate some sort of animation behavior - // (for instance fading) - return NO_ERROR; -} - -status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags) -{ - if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) - return BAD_VALUE; - - Mutex::Autolock _l(mStateLock); - mCurrentState.freezeDisplay = 0; - setTransactionFlags(eTransactionNeeded); - - // flags is intended to communicate some sort of animation behavior - // (for instance fading) - return NO_ERROR; -} - int SurfaceFlinger::setOrientation(DisplayID dpy, int orientation, uint32_t flags) { @@ -1434,11 +1372,6 @@ sp<LayerScreenshot> SurfaceFlinger::createScreenshotSurface( uint32_t w, uint32_t h, uint32_t flags) { sp<LayerScreenshot> layer = new LayerScreenshot(this, display, client); - status_t err = layer->capture(); - if (err != NO_ERROR) { - layer.clear(); - LOGW("createScreenshotSurface failed (%s)", strerror(-err)); - } return layer; } @@ -1512,7 +1445,6 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (what & eSizeChanged) { if (layer->setSize(s.w, s.h)) { flags |= eTraversalNeeded; - mResizeTransationPending = true; } } if (what & eAlphaChanged) { @@ -1605,7 +1537,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) * Dump the layers in the purgatory */ - const size_t purgatorySize = mLayerPurgatory.size(); + const size_t purgatorySize = mLayerPurgatory.size(); snprintf(buffer, SIZE, "Purgatory state (%d entries)\n", purgatorySize); result.append(buffer); for (size_t i=0 ; i<purgatorySize ; i++) { @@ -1626,14 +1558,19 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) extensions.getRenderer(), extensions.getVersion()); result.append(buffer); + + snprintf(buffer, SIZE, "EGL : %s\n", + eglQueryString(graphicPlane(0).getEGLDisplay(), + EGL_VERSION_HW_ANDROID)); + result.append(buffer); + snprintf(buffer, SIZE, "EXTS: %s\n", extensions.getExtension()); result.append(buffer); mWormholeRegion.dump(result, "WormholeRegion"); const DisplayHardware& hw(graphicPlane(0).displayHardware()); snprintf(buffer, SIZE, - " display frozen: %s, freezeCount=%d, orientation=%d, canDraw=%d\n", - mFreezeDisplay?"yes":"no", mFreezeCount, + " orientation=%d, canDraw=%d\n", mCurrentState.orientation, hw.canDraw()); result.append(buffer); snprintf(buffer, SIZE, @@ -1693,8 +1630,6 @@ status_t SurfaceFlinger::onTransact( case CREATE_CONNECTION: case SET_TRANSACTION_STATE: case SET_ORIENTATION: - case FREEZE_DISPLAY: - case UNFREEZE_DISPLAY: case BOOT_FINISHED: case TURN_ELECTRON_BEAM_OFF: case TURN_ELECTRON_BEAM_ON: @@ -1766,10 +1701,6 @@ status_t SurfaceFlinger::onTransact( GraphicLog::getInstance().setEnabled(enabled); return NO_ERROR; } - case 1007: // set mFreezeCount - mFreezeCount = data.readInt32(); - mFreezeDisplayTime = 0; - return NO_ERROR; case 1008: // toggle use of hw composer n = data.readInt32(); mDebugDisableHWC = n ? 1 : 0; @@ -1866,8 +1797,10 @@ status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy, // redraw the screen entirely... glDisable(GL_TEXTURE_EXTERNAL_OES); glDisable(GL_TEXTURE_2D); + glDisable(GL_SCISSOR_TEST); glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); + glEnable(GL_SCISSOR_TEST); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 3284fdb..17b80a6 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -46,7 +46,6 @@ namespace android { class Client; class DisplayHardware; -class FreezeLock; class Layer; class LayerDim; class LayerScreenshot; @@ -169,9 +168,7 @@ public: virtual sp<IMemoryHeap> getCblk() const; virtual void bootFinished(); virtual void setTransactionState(const Vector<ComposerState>& state, - int orientation); - virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags); - virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); + int orientation, uint32_t flags); virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags); virtual bool authenticateSurfaceTexture(const sp<ISurfaceTexture>& surface) const; @@ -189,6 +186,8 @@ public: status_t renderScreenToTexture(DisplayID dpy, GLuint* textureName, GLfloat* uOut, GLfloat* vOut); + status_t renderScreenToTextureLocked(DisplayID dpy, + GLuint* textureName, GLfloat* uOut, GLfloat* vOut); status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime=0, uint32_t flags = 0); @@ -269,12 +268,10 @@ private: struct State { State() { orientation = ISurfaceComposer::eOrientationDefault; - freezeDisplay = 0; } LayerVector layersSortedByZ; uint8_t orientation; uint8_t orientationFlags; - uint8_t freezeDisplay; }; virtual bool threadLoop(); @@ -333,23 +330,7 @@ private: status_t turnElectronBeamOnImplLocked(int32_t mode); status_t electronBeamOffAnimationImplLocked(); status_t electronBeamOnAnimationImplLocked(); - status_t renderScreenToTextureLocked(DisplayID dpy, - GLuint* textureName, GLfloat* uOut, GLfloat* vOut); - friend class FreezeLock; - sp<FreezeLock> getFreezeLock() const; - inline void incFreezeCount() { - if (mFreezeCount == 0) - mFreezeDisplayTime = 0; - mFreezeCount++; - } - inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; } - inline bool hasFreezeRequest() const { return mFreezeDisplay; } - inline bool isFrozen() const { - return (mFreezeDisplay || mFreezeCount>0) && mBootFinished; - } - - void debugFlashRegions(); void debugShowFPS() const; void drawWormhole() const; @@ -363,7 +344,8 @@ private: volatile int32_t mTransactionFlags; Condition mTransactionCV; SortedVector< sp<LayerBase> > mLayerPurgatory; - bool mResizeTransationPending; + bool mTransationPending; + Vector< sp<LayerBase> > mLayersPendingRemoval; // protected by mStateLock (but we could use another lock) GraphicPlane mGraphicPlanes[1]; @@ -390,10 +372,7 @@ private: Region mWormholeRegion; bool mVisibleRegionsDirty; bool mHwWorkListDirty; - bool mFreezeDisplay; int32_t mElectronBeamAnimationMode; - int32_t mFreezeCount; - nsecs_t mFreezeDisplayTime; Vector< sp<LayerBase> > mVisibleLayersSortedByZ; @@ -429,20 +408,6 @@ private: }; // --------------------------------------------------------------------------- - -class FreezeLock : public LightRefBase<FreezeLock> { - SurfaceFlinger* mFlinger; -public: - FreezeLock(SurfaceFlinger* flinger) - : mFlinger(flinger) { - mFlinger->incFreezeCount(); - } - ~FreezeLock() { - mFlinger->decFreezeCount(); - } -}; - -// --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_SURFACE_FLINGER_H diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index 4390ca1..5020e00 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -28,7 +28,7 @@ namespace android { SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer) - : SurfaceTexture(tex), mLayer(layer) { + : SurfaceTexture(tex, true, GL_TEXTURE_EXTERNAL_OES, false), mLayer(layer) { } SurfaceTextureLayer::~SurfaceTextureLayer() { diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk index 5053e7d..b655648 100644 --- a/services/surfaceflinger/tests/Android.mk +++ b/services/surfaceflinger/tests/Android.mk @@ -1 +1,40 @@ -include $(call all-subdir-makefiles) +# Build the unit tests, +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := SurfaceFlinger_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + Transaction_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libEGL \ + libGLESv2 \ + libandroid \ + libbinder \ + libcutils \ + libgui \ + libstlport \ + libui \ + libutils \ + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + +# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +# to integrate with auto-test framework. +include $(BUILD_NATIVE_TEST) + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp new file mode 100644 index 0000000..afafd8a --- /dev/null +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -0,0 +1,236 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> + +#include <binder/IMemory.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <utils/String8.h> + +namespace android { + +// Fill an RGBA_8888 formatted surface with a single color. +static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, + uint8_t r, uint8_t g, uint8_t b) { + Surface::SurfaceInfo info; + sp<Surface> s = sc->getSurface(); + ASSERT_TRUE(s != NULL); + ASSERT_EQ(NO_ERROR, s->lock(&info)); + uint8_t* img = reinterpret_cast<uint8_t*>(info.bits); + for (uint32_t y = 0; y < info.h; y++) { + for (uint32_t x = 0; x < info.w; x++) { + uint8_t* pixel = img + (4 * (y*info.s + x)); + pixel[0] = r; + pixel[1] = g; + pixel[2] = b; + pixel[3] = 255; + } + } + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); +} + +// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check +// individual pixel values for testing purposes. +class ScreenCapture : public RefBase { +public: + static void captureScreen(sp<ScreenCapture>* sc) { + sp<IMemoryHeap> heap; + uint32_t w=0, h=0; + PixelFormat fmt=0; + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 0, 0, + 0, INT_MAX)); + ASSERT_TRUE(heap != NULL); + ASSERT_EQ(PIXEL_FORMAT_RGBA_8888, fmt); + *sc = new ScreenCapture(w, h, heap); + } + + void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) { + const uint8_t* img = reinterpret_cast<const uint8_t*>(mHeap->base()); + const uint8_t* pixel = img + (4 * (y*mWidth + x)); + if (r != pixel[0] || g != pixel[1] || b != pixel[2]) { + String8 err(String8::format("pixel @ (%3d, %3d): " + "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]", + x, y, r, g, b, pixel[0], pixel[1], pixel[2])); + EXPECT_EQ(String8(), err); + } + } + +private: + ScreenCapture(uint32_t w, uint32_t h, const sp<IMemoryHeap>& heap) : + mWidth(w), + mHeight(h), + mHeap(heap) + {} + + const uint32_t mWidth; + const uint32_t mHeight; + sp<IMemoryHeap> mHeap; +}; + +class LayerUpdateTest : public ::testing::Test { +protected: + virtual void SetUp() { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + ssize_t displayWidth = mComposerClient->getDisplayWidth(0); + ssize_t displayHeight = mComposerClient->getDisplayHeight(0); + + // Background surface + mBGSurfaceControl = mComposerClient->createSurface( + String8("BG Test Surface"), 0, displayWidth, displayHeight, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mBGSurfaceControl != NULL); + ASSERT_TRUE(mBGSurfaceControl->isValid()); + fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195); + + // Foreground surface + mFGSurfaceControl = mComposerClient->createSurface( + String8("FG Test Surface"), 0, 64, 64, PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mFGSurfaceControl != NULL); + ASSERT_TRUE(mFGSurfaceControl->isValid()); + + fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + + // Synchronization surface + mSyncSurfaceControl = mComposerClient->createSurface( + String8("Sync Test Surface"), 0, 1, 1, PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mSyncSurfaceControl != NULL); + ASSERT_TRUE(mSyncSurfaceControl->isValid()); + + fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + + SurfaceComposerClient::openGlobalTransaction(); + + ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-2)); + ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show()); + + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX-1)); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64)); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show()); + + ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT_MAX-1)); + ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setPosition(displayWidth-2, + displayHeight-2)); + ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->show()); + + SurfaceComposerClient::closeGlobalTransaction(true); + } + + virtual void TearDown() { + mComposerClient->dispose(); + mBGSurfaceControl = 0; + mFGSurfaceControl = 0; + mSyncSurfaceControl = 0; + mComposerClient = 0; + } + + void waitForPostedBuffers() { + // Since the sync surface is in synchronous mode (i.e. double buffered) + // posting three buffers to it should ensure that at least two + // SurfaceFlinger::handlePageFlip calls have been made, which should + // guaranteed that a buffer posted to another Surface has been retired. + fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + } + + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mBGSurfaceControl; + sp<SurfaceControl> mFGSurfaceControl; + + // This surface is used to ensure that the buffers posted to + // mFGSurfaceControl have been picked up by SurfaceFlinger. + sp<SurfaceControl> mSyncSurfaceControl; +}; + +TEST_F(LayerUpdateTest, LayerMoveWorks) { + sp<ScreenCapture> sc; + { + SCOPED_TRACE("before move"); + ScreenCapture::captureScreen(&sc); + sc->checkPixel( 0, 12, 63, 63, 195); + sc->checkPixel( 75, 75, 195, 63, 63); + sc->checkPixel(145, 145, 63, 63, 195); + } + + SurfaceComposerClient::openGlobalTransaction(); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128)); + SurfaceComposerClient::closeGlobalTransaction(true); + { + // This should reflect the new position, but not the new color. + SCOPED_TRACE("after move, before redraw"); + ScreenCapture::captureScreen(&sc); + sc->checkPixel( 24, 24, 63, 63, 195); + sc->checkPixel( 75, 75, 63, 63, 195); + sc->checkPixel(145, 145, 195, 63, 63); + } + + fillSurfaceRGBA8(mFGSurfaceControl, 63, 195, 63); + waitForPostedBuffers(); + { + // This should reflect the new position and the new color. + SCOPED_TRACE("after redraw"); + ScreenCapture::captureScreen(&sc); + sc->checkPixel( 24, 24, 63, 63, 195); + sc->checkPixel( 75, 75, 63, 63, 195); + sc->checkPixel(145, 145, 63, 195, 63); + } +} + +TEST_F(LayerUpdateTest, LayerResizeWorks) { + sp<ScreenCapture> sc; + { + SCOPED_TRACE("before resize"); + ScreenCapture::captureScreen(&sc); + sc->checkPixel( 0, 12, 63, 63, 195); + sc->checkPixel( 75, 75, 195, 63, 63); + sc->checkPixel(145, 145, 63, 63, 195); + } + + LOGD("resizing"); + SurfaceComposerClient::openGlobalTransaction(); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128)); + SurfaceComposerClient::closeGlobalTransaction(true); + LOGD("resized"); + { + // This should not reflect the new size or color because SurfaceFlinger + // has not yet received a buffer of the correct size. + SCOPED_TRACE("after resize, before redraw"); + ScreenCapture::captureScreen(&sc); + sc->checkPixel( 0, 12, 63, 63, 195); + sc->checkPixel( 75, 75, 195, 63, 63); + sc->checkPixel(145, 145, 63, 63, 195); + } + + LOGD("drawing"); + fillSurfaceRGBA8(mFGSurfaceControl, 63, 195, 63); + waitForPostedBuffers(); + LOGD("drawn"); + { + // This should reflect the new size and the new color. + SCOPED_TRACE("after redraw"); + ScreenCapture::captureScreen(&sc); + sc->checkPixel( 24, 24, 63, 63, 195); + sc->checkPixel( 75, 75, 63, 195, 63); + sc->checkPixel(145, 145, 63, 195, 63); + } +} + +} diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index e892b5e..7c61e9a 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -66,6 +66,7 @@ import android.net.NetworkTemplate; import android.os.Binder; import android.os.INetworkManagementService; import android.os.IPowerManager; +import android.os.MessageQueue.IdleHandler; import android.test.AndroidTestCase; import android.test.mock.MockPackageManager; import android.test.suitebuilder.annotation.LargeTest; @@ -79,7 +80,6 @@ import com.google.common.util.concurrent.AbstractFuture; import org.easymock.Capture; import org.easymock.EasyMock; import org.easymock.IAnswer; -import org.easymock.IExpectationSetters; import java.io.File; import java.util.LinkedHashSet; @@ -87,6 +87,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.logging.Handler; import libcore.io.IoUtils; @@ -100,6 +101,10 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { private static final long TEST_START = 1194220800000L; private static final String TEST_IFACE = "test0"; + private static final long KB_IN_BYTES = 1024; + private static final long MB_IN_BYTES = KB_IN_BYTES * 1024; + private static final long GB_IN_BYTES = MB_IN_BYTES * 1024; + private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(); private BroadcastInterceptingContext mServiceContext; @@ -255,31 +260,37 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false); mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false); + waitUntilIdle(); assertFalse(mService.isUidForeground(UID_A)); assertFalse(mService.isUidForeground(UID_B)); // push one of the shared pids into foreground mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true); + waitUntilIdle(); assertTrue(mService.isUidForeground(UID_A)); assertFalse(mService.isUidForeground(UID_B)); // and swap another uid into foreground mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false); mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true); + waitUntilIdle(); assertFalse(mService.isUidForeground(UID_A)); assertTrue(mService.isUidForeground(UID_B)); // push both pid into foreground mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true); mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true); + waitUntilIdle(); assertTrue(mService.isUidForeground(UID_A)); // pull one out, should still be foreground mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false); + waitUntilIdle(); assertTrue(mService.isUidForeground(UID_A)); // pull final pid out, should now be background mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false); + waitUntilIdle(); assertFalse(mService.isUidForeground(UID_A)); } @@ -525,16 +536,18 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L); expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10)) .andReturn(stats).atLeastOnce(); + expectPolicyDataEnable(TYPE_WIFI, true); // TODO: consider making strongly ordered mock expectRemoveInterfaceQuota(TEST_IFACE); - expectSetInterfaceQuota(TEST_IFACE, 1536L); + expectSetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512); expectClearNotifications(); future = expectMeteredIfacesChanged(TEST_IFACE); replay(); - setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L, SNOOZE_NEVER)); + setNetworkPolicies(new NetworkPolicy( + sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER)); future.get(); verifyAndReset(); } @@ -567,7 +580,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { NetworkState[] state = null; NetworkStats stats = null; Future<Void> future; - Capture<String> tag; + Future<String> tagFuture; final long TIME_FEB_15 = 1171497600000L; final long TIME_MAR_10 = 1173484800000L; @@ -585,13 +598,14 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis())) .andReturn(stats).atLeastOnce(); + expectPolicyDataEnable(TYPE_WIFI, true); expectClearNotifications(); future = expectMeteredIfacesChanged(); replay(); - setNetworkPolicies( - new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L, SNOOZE_NEVER)); + setNetworkPolicies(new NetworkPolicy( + sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER)); future.get(); verifyAndReset(); } @@ -607,9 +621,10 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis())) .andReturn(stats).atLeastOnce(); + expectPolicyDataEnable(TYPE_WIFI, true); expectRemoveInterfaceQuota(TEST_IFACE); - expectSetInterfaceQuota(TEST_IFACE, 2048L); + expectSetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES); expectClearNotifications(); future = expectMeteredIfacesChanged(TEST_IFACE); @@ -623,41 +638,42 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // go over warning, which should kick notification incrementCurrentTime(MINUTE_IN_MILLIS); stats = new NetworkStats(getElapsedRealtime(), 1) - .addIfaceValues(TEST_IFACE, 1536L, 15L, 0L, 0L); + .addIfaceValues(TEST_IFACE, 1536 * KB_IN_BYTES, 15L, 0L, 0L); { expectCurrentTime(); expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis())) .andReturn(stats).atLeastOnce(); + expectPolicyDataEnable(TYPE_WIFI, true); expectForceUpdate(); expectClearNotifications(); - tag = expectEnqueueNotification(); + tagFuture = expectEnqueueNotification(); replay(); mNetworkObserver.limitReached(null, TEST_IFACE); - assertNotificationType(TYPE_WARNING, tag.getValue()); + assertNotificationType(TYPE_WARNING, tagFuture.get()); verifyAndReset(); } // go over limit, which should kick notification and dialog incrementCurrentTime(MINUTE_IN_MILLIS); stats = new NetworkStats(getElapsedRealtime(), 1) - .addIfaceValues(TEST_IFACE, 5120L, 512L, 0L, 0L); + .addIfaceValues(TEST_IFACE, 5 * MB_IN_BYTES, 512L, 0L, 0L); { expectCurrentTime(); expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis())) .andReturn(stats).atLeastOnce(); - expectPolicyDataEnable(TYPE_WIFI, false).atLeastOnce(); + expectPolicyDataEnable(TYPE_WIFI, false); expectForceUpdate(); expectClearNotifications(); - tag = expectEnqueueNotification(); + tagFuture = expectEnqueueNotification(); replay(); mNetworkObserver.limitReached(null, TEST_IFACE); - assertNotificationType(TYPE_LIMIT, tag.getValue()); + assertNotificationType(TYPE_LIMIT, tagFuture.get()); verifyAndReset(); } @@ -669,21 +685,21 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis())) .andReturn(stats).atLeastOnce(); - expectPolicyDataEnable(TYPE_WIFI, true).atLeastOnce(); + expectPolicyDataEnable(TYPE_WIFI, true); // snoozed interface still has high quota so background data is // still restricted. expectRemoveInterfaceQuota(TEST_IFACE); expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE); + expectMeteredIfacesChanged(TEST_IFACE); - expectClearNotifications(); - tag = expectEnqueueNotification(); - future = expectMeteredIfacesChanged(TEST_IFACE); + future = expectClearNotifications(); + tagFuture = expectEnqueueNotification(); replay(); mService.snoozePolicy(sTemplateWifi); + assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get()); future.get(); - assertNotificationType(TYPE_LIMIT_SNOOZED, tag.getValue()); verifyAndReset(); } } @@ -719,14 +735,16 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expectLastCall().atLeastOnce(); } - private void expectClearNotifications() throws Exception { + private Future<Void> expectClearNotifications() throws Exception { + final FutureAnswer future = new FutureAnswer(); mNotifManager.cancelNotificationWithTag(isA(String.class), isA(String.class), anyInt()); - expectLastCall().anyTimes(); + expectLastCall().andAnswer(future).anyTimes(); + return future; } - private Capture<String> expectEnqueueNotification() throws Exception { - final Capture<String> tag = new Capture<String>(); - mNotifManager.enqueueNotificationWithTag(isA(String.class), capture(tag), anyInt(), + private Future<String> expectEnqueueNotification() throws Exception { + final FutureCapture<String> tag = new FutureCapture<String>(); + mNotifManager.enqueueNotificationWithTag(isA(String.class), capture(tag.capture), anyInt(), isA(Notification.class), isA(int[].class)); return tag; } @@ -776,22 +794,25 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { return future; } - private <T> IExpectationSetters<T> expectPolicyDataEnable(int type, boolean enabled) - throws Exception { + private Future<Void> expectPolicyDataEnable(int type, boolean enabled) throws Exception { + final FutureAnswer future = new FutureAnswer(); mConnManager.setPolicyDataEnable(type, enabled); - return expectLastCall(); + expectLastCall().andAnswer(future); + return future; } - private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> { + private static class TestAbstractFuture<T> extends AbstractFuture<T> { @Override - public Void get() throws InterruptedException, ExecutionException { + public T get() throws InterruptedException, ExecutionException { try { return get(5, TimeUnit.SECONDS); } catch (TimeoutException e) { throw new RuntimeException(e); } } + } + private static class FutureAnswer extends TestAbstractFuture<Void> implements IAnswer<Void> { @Override public Void answer() { set(null); @@ -799,6 +820,42 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } } + private static class FutureCapture<T> extends TestAbstractFuture<T> { + public Capture<T> capture = new Capture<T>() { + @Override + public void setValue(T value) { + super.setValue(value); + set(value); + } + }; + } + + private static class IdleFuture extends AbstractFuture<Void> implements IdleHandler { + @Override + public Void get() throws InterruptedException, ExecutionException { + try { + return get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + + /** {@inheritDoc} */ + public boolean queueIdle() { + set(null); + return false; + } + } + + /** + * Wait until {@link #mService} internal {@link Handler} is idle. + */ + private void waitUntilIdle() throws Exception { + final IdleFuture future = new IdleFuture(); + mService.addIdleHandler(future); + future.get(); + } + private static void assertTimeEquals(long expected, long actual) { if (expected != actual) { fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual)); diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index f7dff23..fbc171b 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -83,6 +83,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private static final String TAG = "NetworkStatsServiceTest"; private static final String TEST_IFACE = "test0"; + private static final String TEST_IFACE2 = "test1"; private static final long TEST_START = 1194220800000L; private static final String IMSI_1 = "310004"; @@ -418,8 +419,12 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectCurrentTime(); expectDefaultSettings(); expectNetworkState(buildMobile3gState(IMSI_2)); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L)); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); expectNetworkStatsPoll(); replay(); @@ -432,9 +437,11 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectCurrentTime(); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .addIfaceValues(TEST_IFACE, 128L, 1L, 1024L, 8L)); + .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L)); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 1024L, 8L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L) .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L)); expectNetworkStatsPoll(); @@ -499,6 +506,15 @@ public class NetworkStatsServiceTest extends AndroidTestCase { // special "removed" bucket. expectCurrentTime(); expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L)); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) + .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L) + .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); + expectNetworkStatsPoll(); + replay(); final Intent intent = new Intent(ACTION_UID_REMOVED); intent.putExtra(EXTRA_UID, UID_BLUE); @@ -553,9 +569,11 @@ public class NetworkStatsServiceTest extends AndroidTestCase { incrementCurrentTime(HOUR_IN_MILLIS); expectCurrentTime(); expectDefaultSettings(); - expectNetworkState(buildMobile4gState()); + expectNetworkState(buildMobile4gState(TEST_IFACE2)); expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); expectNetworkStatsPoll(); replay(); @@ -569,8 +587,10 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L)); + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) + .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L)); expectNetworkStatsPoll(); mService.incrementOperationCount(UID_RED, 0xFAAD, 5); @@ -625,6 +645,8 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L)); expectNetworkStatsPoll(); @@ -881,11 +903,11 @@ public class NetworkStatsServiceTest extends AndroidTestCase { return new NetworkState(info, prop, null, subscriberId); } - private static NetworkState buildMobile4gState() { + private static NetworkState buildMobile4gState(String iface) { final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null); info.setDetailedState(DetailedState.CONNECTED, null, null); final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(TEST_IFACE); + prop.setInterfaceName(iface); return new NetworkState(info, prop, null); } |