summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/Android.mk4
-rw-r--r--services/audioflinger/AudioFlinger.cpp634
-rw-r--r--services/audioflinger/AudioFlinger.h228
-rw-r--r--services/audioflinger/AudioMixer.cpp526
-rw-r--r--services/audioflinger/AudioMixer.h45
-rw-r--r--services/audioflinger/AudioPolicyService.cpp100
-rw-r--r--services/audioflinger/AudioPolicyService.h37
-rw-r--r--services/audioflinger/AudioResampler.cpp5
-rw-r--r--services/audioflinger/AudioResamplerSinc.cpp14
-rw-r--r--services/audioflinger/AudioResamplerSinc.h8
-rw-r--r--services/camera/libcameraservice/CameraHardwareInterface.h7
-rw-r--r--services/camera/libcameraservice/CameraService.cpp2
-rw-r--r--services/input/EventHub.cpp32
-rw-r--r--services/input/EventHub.h3
-rw-r--r--services/input/InputDispatcher.cpp12
-rw-r--r--services/input/InputDispatcher.h2
-rw-r--r--services/input/InputReader.cpp31
-rw-r--r--services/input/InputReader.h2
-rw-r--r--services/java/com/android/server/AppWidgetService.java1491
-rw-r--r--services/java/com/android/server/AppWidgetServiceImpl.java1606
-rw-r--r--services/java/com/android/server/BackupManagerService.java186
-rw-r--r--services/java/com/android/server/ConnectivityService.java42
-rw-r--r--services/java/com/android/server/EntropyMixer.java (renamed from services/java/com/android/server/EntropyService.java)8
-rw-r--r--services/java/com/android/server/EventLogTags.logtags4
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java31
-rw-r--r--services/java/com/android/server/LocationManagerService.java6
-rw-r--r--services/java/com/android/server/MountService.java211
-rw-r--r--services/java/com/android/server/NativeDaemonConnector.java394
-rw-r--r--services/java/com/android/server/NativeDaemonConnectorException.java36
-rw-r--r--services/java/com/android/server/NativeDaemonEvent.java142
-rw-r--r--services/java/com/android/server/NetworkManagementService.java984
-rw-r--r--services/java/com/android/server/NetworkTimeUpdateService.java5
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java3
-rw-r--r--services/java/com/android/server/PowerManagerService.java200
-rw-r--r--services/java/com/android/server/SystemServer.java4
-rw-r--r--services/java/com/android/server/UiModeManagerService.java7
-rw-r--r--services/java/com/android/server/WifiService.java35
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java136
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java2601
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java5
-rw-r--r--services/java/com/android/server/am/ActivityStack.java70
-rw-r--r--services/java/com/android/server/am/BroadcastRecord.java5
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java4
-rw-r--r--services/java/com/android/server/am/ProviderMap.java244
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java4
-rw-r--r--services/java/com/android/server/am/TaskRecord.java11
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java9
-rw-r--r--services/java/com/android/server/connectivity/Vpn.java8
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java9
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java147
-rw-r--r--services/java/com/android/server/net/NetworkStatsCollection.java510
-rw-r--r--services/java/com/android/server/net/NetworkStatsRecorder.java341
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java1308
-rw-r--r--services/java/com/android/server/pm/Installer.java21
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java81
-rw-r--r--services/java/com/android/server/pm/Settings.java16
-rw-r--r--services/java/com/android/server/pm/UserManager.java50
-rw-r--r--services/java/com/android/server/wm/DragState.java10
-rw-r--r--services/java/com/android/server/wm/FakeWindowImpl.java12
-rw-r--r--services/java/com/android/server/wm/ScreenRotationAnimation.java416
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java185
-rw-r--r--services/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/jni/com_android_server_PowerManagerService.cpp2
-rw-r--r--services/sensorservice/SensorService.cpp4
-rw-r--r--services/sensorservice/SensorService.h6
-rw-r--r--services/surfaceflinger/Android.mk34
-rw-r--r--services/surfaceflinger/DisplayEventConnection.cpp71
-rw-r--r--services/surfaceflinger/DisplayEventConnection.h62
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.cpp51
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.h18
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp127
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h43
-rw-r--r--services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp81
-rw-r--r--services/surfaceflinger/DisplayHardware/VSyncBarrier.h41
-rw-r--r--services/surfaceflinger/EventThread.cpp231
-rw-r--r--services/surfaceflinger/EventThread.h110
-rw-r--r--services/surfaceflinger/Layer.cpp80
-rw-r--r--services/surfaceflinger/Layer.h22
-rw-r--r--services/surfaceflinger/LayerBase.cpp43
-rw-r--r--services/surfaceflinger/LayerBase.h21
-rw-r--r--services/surfaceflinger/MessageQueue.cpp230
-rw-r--r--services/surfaceflinger/MessageQueue.h115
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp599
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h27
-rw-r--r--services/surfaceflinger/SurfaceTextureLayer.cpp4
-rw-r--r--services/surfaceflinger/tests/vsync/Android.mk18
-rw-r--r--services/surfaceflinger/tests/vsync/vsync.cpp83
-rw-r--r--services/surfaceflinger/tests/waitforvsync/Android.mk14
-rw-r--r--services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp45
-rw-r--r--services/tests/servicestests/res/raw/netstats_uid_v4bin0 -> 156516 bytes
-rw-r--r--services/tests/servicestests/res/raw/netstats_v1bin0 -> 18742 bytes
-rw-r--r--services/tests/servicestests/src/com/android/server/EntropyMixerTest.java (renamed from services/tests/servicestests/src/com/android/server/EntropyServiceTest.java)6
-rw-r--r--services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java175
96 files changed, 9595 insertions, 6295 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index fa49592..52834db 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -11,9 +11,11 @@ LOCAL_SRC_FILES:= \
AudioPolicyService.cpp
LOCAL_C_INCLUDES := \
- system/media/audio_effects/include
+ system/media/audio_effects/include \
+ system/media/audio_utils/include
LOCAL_SHARED_LIBRARIES := \
+ libaudioutils \
libcutils \
libutils \
libbinder \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 5a46e44..f71ba0a 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -35,10 +35,10 @@
#include <cutils/bitops.h>
#include <cutils/properties.h>
+#include <cutils/compiler.h>
-#include <media/AudioTrack.h>
-#include <media/AudioRecord.h>
#include <media/IMediaPlayerService.h>
+#include <media/IMediaDeathNotifier.h>
#include <private/media/AudioTrackShared.h>
#include <private/media/AudioEffectShared.h>
@@ -54,6 +54,8 @@
#include <audio_effects/effect_ns.h>
#include <audio_effects/effect_aec.h>
+#include <audio_utils/primitives.h>
+
#include <cpustats/ThreadCpuUsage.h>
#include <powermanager/PowerManager.h>
// #define DEBUG_CPU_USAGE 10 // log statistics every n wall clock seconds
@@ -63,12 +65,12 @@
namespace android {
-static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n";
-static const char* kHardwareLockedString = "Hardware lock is taken\n";
+static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
+static const char kHardwareLockedString[] = "Hardware lock is taken\n";
//static const nsecs_t kStandbyTimeInNsecs = seconds(3);
static const float MAX_GAIN = 4096.0f;
-static const float MAX_GAIN_INT = 0x1000;
+static const uint32_t MAX_GAIN_INT = 0x1000;
// retry counts for buffer fill timeout
// 50 * ~20msecs = 1 second
@@ -80,14 +82,16 @@ static const int8_t kMaxTrackStartupRetries = 50;
static const int8_t kMaxTrackRetriesDirect = 2;
static const int kDumpLockRetries = 50;
-static const int kDumpLockSleep = 20000;
+static const int kDumpLockSleepUs = 20000;
-static const nsecs_t kWarningThrottle = seconds(5);
+// don't warn about blocked writes or record buffer overflows more often than this
+static const nsecs_t kWarningThrottleNs = seconds(5);
// RecordThread loop sleep time upon application overrun or audio HAL read error
static const int kRecordThreadSleepUs = 5000;
-static const nsecs_t kSetParametersTimeout = seconds(2);
+// maximum time to wait for setParameters to complete
+static const nsecs_t kSetParametersTimeoutNs = seconds(2);
// minimum sleep time for the mixer thread loop when tracks are active but in underrun
static const uint32_t kMinThreadSleepTimeUs = 5000;
@@ -113,11 +117,9 @@ static bool settingsAllowed() {
// To collect the amplifier usage
static void addBatteryData(uint32_t params) {
- sp<IBinder> binder =
- defaultServiceManager()->getService(String16("media.player"));
- sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
- if (service.get() == NULL) {
- ALOGW("Cannot connect to the MediaPlayerService for battery tracking");
+ sp<IMediaPlayerService> service = IMediaDeathNotifier::getMediaPlayerService();
+ if (service == NULL) {
+ // it already logged
return;
}
@@ -147,7 +149,7 @@ out:
return rc;
}
-static const char *audio_interfaces[] = {
+static const char * const audio_interfaces[] = {
"primary",
"a2dp",
"usb",
@@ -158,7 +160,10 @@ static const char *audio_interfaces[] = {
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mPrimaryHardwareDev(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+ mPrimaryHardwareDev(NULL),
+ mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef()
+ mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+ mMode(AUDIO_MODE_INVALID),
mBtNrecIsOff(false)
{
}
@@ -170,7 +175,6 @@ void AudioFlinger::onFirstRef()
Mutex::Autolock _l(mLock);
/* TODO: move all this work into an Init() function */
- mHardwareStatus = AUDIO_HW_IDLE;
for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
const hw_module_t *mod;
@@ -263,13 +267,10 @@ status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
result.append("Clients:\n");
for (size_t i = 0; i < mClients.size(); ++i) {
- wp<Client> wClient = mClients.valueAt(i);
- if (wClient != 0) {
- sp<Client> client = wClient.promote();
- if (client != 0) {
- snprintf(buffer, SIZE, " pid: %d\n", client->pid());
- result.append(buffer);
- }
+ sp<Client> client = mClients.valueAt(i).promote();
+ if (client != 0) {
+ snprintf(buffer, SIZE, " pid: %d\n", client->pid());
+ result.append(buffer);
}
}
@@ -290,7 +291,7 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
- int hardwareStatus = mHardwareStatus;
+ hardware_call_state hardwareStatus = mHardwareStatus;
snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus);
result.append(buffer);
@@ -320,14 +321,14 @@ static bool tryLock(Mutex& mutex)
locked = true;
break;
}
- usleep(kDumpLockSleep);
+ usleep(kDumpLockSleepUs);
}
return locked;
}
status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
{
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ if (!checkCallingPermission(String16("android.permission.DUMP"))) {
dumpPermissionDenial(fd, args);
} else {
// get state of hardware lock
@@ -376,9 +377,9 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
sp<IAudioTrack> AudioFlinger::createTrack(
pid_t pid,
- int streamType,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
uint32_t flags,
@@ -394,8 +395,10 @@ sp<IAudioTrack> AudioFlinger::createTrack(
status_t lStatus;
int lSessionId;
- if (streamType >= AUDIO_STREAM_CNT) {
- ALOGE("invalid stream type");
+ // client AudioTrack::set already implements AUDIO_STREAM_DEFAULT => AUDIO_STREAM_MUSIC,
+ // but if someone uses binder directly they could bypass that and cause us to crash
+ if (uint32_t(streamType) >= AUDIO_STREAM_CNT) {
+ ALOGE("createTrack() invalid stream type %d", streamType);
lStatus = BAD_VALUE;
goto Exit;
}
@@ -427,6 +430,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
// prevent same audio session on different output threads
uint32_t sessions = t->hasAudioSession(*sessionId);
if (sessions & PlaybackThread::TRACK_SESSION) {
+ ALOGE("createTrack() session ID %d already in use", *sessionId);
lStatus = BAD_VALUE;
goto Exit;
}
@@ -495,13 +499,13 @@ int AudioFlinger::channelCount(int output) const
return thread->channelCount();
}
-uint32_t AudioFlinger::format(int output) const
+audio_format_t AudioFlinger::format(int output) const
{
Mutex::Autolock _l(mLock);
PlaybackThread *thread = checkPlaybackThread_l(output);
if (thread == NULL) {
ALOGW("format() unknown thread %d", output);
- return 0;
+ return AUDIO_FORMAT_INVALID;
}
return thread->format();
}
@@ -558,7 +562,7 @@ status_t AudioFlinger::setMasterVolume(float value)
return NO_ERROR;
}
-status_t AudioFlinger::setMode(int mode)
+status_t AudioFlinger::setMode(audio_mode_t mode)
{
status_t ret = initCheck();
if (ret != NO_ERROR) {
@@ -569,7 +573,7 @@ status_t AudioFlinger::setMode(int mode)
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
- if ((mode < 0) || (mode >= AUDIO_MODE_CNT)) {
+ if (uint32_t(mode) >= AUDIO_MODE_CNT) {
ALOGW("Illegal value: setMode(%d)", mode);
return BAD_VALUE;
}
@@ -641,22 +645,25 @@ status_t AudioFlinger::setMasterMute(bool muted)
float AudioFlinger::masterVolume() const
{
- return mMasterVolume;
+ Mutex::Autolock _l(mLock);
+ return masterVolume_l();
}
bool AudioFlinger::masterMute() const
{
- return mMasterMute;
+ Mutex::Autolock _l(mLock);
+ return masterMute_l();
}
-status_t AudioFlinger::setStreamVolume(int stream, float value, int output)
+status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, int output)
{
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) {
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
+ ALOGE("setStreamVolume() invalid stream %d", stream);
return BAD_VALUE;
}
@@ -682,15 +689,16 @@ status_t AudioFlinger::setStreamVolume(int stream, float value, int output)
return NO_ERROR;
}
-status_t AudioFlinger::setStreamMute(int stream, bool muted)
+status_t AudioFlinger::setStreamMute(audio_stream_type_t stream, bool muted)
{
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT ||
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT ||
uint32_t(stream) == AUDIO_STREAM_ENFORCED_AUDIBLE) {
+ ALOGE("setStreamMute() invalid stream %d", stream);
return BAD_VALUE;
}
@@ -702,9 +710,9 @@ status_t AudioFlinger::setStreamMute(int stream, bool muted)
return NO_ERROR;
}
-float AudioFlinger::streamVolume(int stream, int output) const
+float AudioFlinger::streamVolume(audio_stream_type_t stream, int output) const
{
- if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) {
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
return 0.0f;
}
@@ -723,9 +731,9 @@ float AudioFlinger::streamVolume(int stream, int output) const
return volume;
}
-bool AudioFlinger::streamMute(int stream) const
+bool AudioFlinger::streamMute(audio_stream_type_t stream) const
{
- if (stream < 0 || stream >= (int)AUDIO_STREAM_CNT) {
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
return true;
}
@@ -790,7 +798,7 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
thread = checkPlaybackThread_l(ioHandle);
if (thread == NULL) {
thread = checkRecordThread_l(ioHandle);
- } else if (thread.get() == primaryPlaybackThread_l()) {
+ } else if (thread == primaryPlaybackThread_l()) {
// indicate output device change to all input threads for pre processing
AudioParameter param = AudioParameter(keyValuePairs);
int value;
@@ -838,7 +846,7 @@ String8 AudioFlinger::getParameters(int ioHandle, const String8& keys)
return String8("");
}
-size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount)
{
status_t ret = initCheck();
if (ret != NO_ERROR) {
@@ -962,7 +970,8 @@ void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2)
{
size_t size = mNotificationClients.size();
for (size_t i = 0; i < size; i++) {
- mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2);
+ mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle,
+ param2);
}
}
@@ -976,19 +985,24 @@ void AudioFlinger::removeClient_l(pid_t pid)
// ----------------------------------------------------------------------------
-AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device)
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device,
+ type_t type)
: Thread(false),
- mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
- mFrameSize(1), mFormat(0), mStandby(false), mId(id), mExiting(false),
- mDevice(device)
+ mType(type),
+ mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0),
+ // mChannelMask
+ mChannelCount(0),
+ mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
+ mParamStatus(NO_ERROR),
+ mStandby(false), mId(id), mExiting(false),
+ mDevice(device),
+ mDeathRecipient(new PMDeathRecipient(this))
{
- mDeathRecipient = new PMDeathRecipient(this);
}
AudioFlinger::ThreadBase::~ThreadBase()
{
mParamCond.broadcast();
- mNewParameters.clear();
// do not lock the mutex in destructor
releaseWakeLock_l();
if (mPowerManager != 0) {
@@ -999,13 +1013,13 @@ AudioFlinger::ThreadBase::~ThreadBase()
void AudioFlinger::ThreadBase::exit()
{
- // keep a strong ref on ourself so that we wont get
+ // keep a strong ref on ourself so that we won't get
// destroyed in the middle of requestExitAndWait()
sp <ThreadBase> strongMe = this;
ALOGV("ThreadBase::exit");
{
- AutoMutex lock(&mLock);
+ AutoMutex lock(mLock);
mExiting = true;
requestExit();
mWaitWorkCV.signal();
@@ -1023,7 +1037,7 @@ int AudioFlinger::ThreadBase::channelCount() const
return (int)mChannelCount;
}
-uint32_t AudioFlinger::ThreadBase::format() const
+audio_format_t AudioFlinger::ThreadBase::format() const
{
return mFormat;
}
@@ -1044,7 +1058,7 @@ status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
mWaitWorkCV.signal();
// wait condition with timeout in case the thread loop has exited
// before the request could be processed
- if (mParamCond.waitRelative(mLock, kSetParametersTimeout) == NO_ERROR) {
+ if (mParamCond.waitRelative(mLock, kSetParametersTimeoutNs) == NO_ERROR) {
status = mParamStatus;
mWaitWorkCV.signal();
} else {
@@ -1062,9 +1076,9 @@ void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param)
// sendConfigEvent_l() must be called with ThreadBase::mLock held
void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param)
{
- ConfigEvent *configEvent = new ConfigEvent();
- configEvent->mEvent = event;
- configEvent->mParam = param;
+ ConfigEvent configEvent;
+ configEvent.mEvent = event;
+ configEvent.mParam = param;
mConfigEvents.add(configEvent);
ALOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param);
mWaitWorkCV.signal();
@@ -1075,15 +1089,14 @@ void AudioFlinger::ThreadBase::processConfigEvents()
mLock.lock();
while(!mConfigEvents.isEmpty()) {
ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
- ConfigEvent *configEvent = mConfigEvents[0];
+ ConfigEvent configEvent = mConfigEvents[0];
mConfigEvents.removeAt(0);
// release mLock before locking AudioFlinger mLock: lock order is always
// AudioFlinger then ThreadBase to avoid cross deadlock
mLock.unlock();
mAudioFlinger->mLock.lock();
- audioConfigChanged_l(configEvent->mEvent, configEvent->mParam);
+ audioConfigChanged_l(configEvent.mEvent, configEvent.mParam);
mAudioFlinger->mLock.unlock();
- delete configEvent;
mLock.lock();
}
mLock.unlock();
@@ -1113,7 +1126,7 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args
result.append(buffer);
snprintf(buffer, SIZE, "Format: %d\n", mFormat);
result.append(buffer);
- snprintf(buffer, SIZE, "Frame size: %d\n", mFrameSize);
+ snprintf(buffer, SIZE, "Frame size: %u\n", mFrameSize);
result.append(buffer);
snprintf(buffer, SIZE, "\nPending setParameters commands: \n");
@@ -1130,7 +1143,7 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args
snprintf(buffer, SIZE, " Index event param\n");
result.append(buffer);
for (size_t i = 0; i < mConfigEvents.size(); i++) {
- snprintf(buffer, SIZE, " %02d %02d %d\n", i, mConfigEvents[i]->mEvent, mConfigEvents[i]->mParam);
+ snprintf(buffer, SIZE, " %02d %02d %d\n", i, mConfigEvents[i].mEvent, mConfigEvents[i].mParam);
result.append(buffer);
}
result.append("\n");
@@ -1365,22 +1378,32 @@ void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModu
AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
int id,
- uint32_t device)
- : ThreadBase(audioFlinger, id, device),
- mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output),
+ uint32_t device,
+ type_t type)
+ : ThreadBase(audioFlinger, id, device, type),
+ mMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+ // Assumes constructor is called by AudioFlinger with it's mLock held,
+ // but it would be safer to explicitly pass initial masterMute as parameter
+ mMasterMute(audioFlinger->masterMute_l()),
+ // mStreamTypes[] initialized in constructor body
+ mOutput(output),
+ // Assumes constructor is called by AudioFlinger with it's mLock held,
+ // but it would be safer to explicitly pass initial masterVolume as parameter
+ mMasterVolume(audioFlinger->masterVolume_l()),
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
{
snprintf(mName, kNameLength, "AudioOut_%d", id);
readOutputParameters();
- mMasterVolume = mAudioFlinger->masterVolume();
- mMasterMute = mAudioFlinger->masterMute();
-
- for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
+ // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
+ // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
+ for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT;
+ stream = (audio_stream_type_t) (stream + 1)) {
mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
- mStreamTypes[stream].valid = true;
+ // initialized by stream_type_t default constructor
+ // mStreamTypes[stream].valid = true;
}
}
@@ -1418,13 +1441,10 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>
result.append(buffer);
result.append(" Name Clien Typ Fmt Chn mask Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n");
for (size_t i = 0; i < mActiveTracks.size(); ++i) {
- wp<Track> wTrack = mActiveTracks[i];
- if (wTrack != 0) {
- sp<Track> track = wTrack.promote();
- if (track != 0) {
- track->dump(buffer, SIZE);
- result.append(buffer);
- }
+ sp<Track> track = mActiveTracks[i].promote();
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
}
}
write(fd, result.string(), result.size());
@@ -1478,9 +1498,9 @@ void AudioFlinger::PlaybackThread::onFirstRef()
// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
const sp<AudioFlinger::Client>& client,
- int streamType,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
@@ -1526,8 +1546,10 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> t = mTracks[i];
if (t != 0) {
- if (sessionId == t->sessionId() &&
- strategy != AudioSystem::getStrategyForStream((audio_stream_type_t)t->type())) {
+ uint32_t actual = AudioSystem::getStrategyForStream((audio_stream_type_t)t->type());
+ if (sessionId == t->sessionId() && strategy != actual) {
+ ALOGE("createTrack_l() mismatched strategy; expected %u but found %u",
+ strategy, actual);
lStatus = BAD_VALUE;
goto Exit;
}
@@ -1599,24 +1621,24 @@ bool AudioFlinger::PlaybackThread::masterMute() const
return mMasterMute;
}
-status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value)
+status_t AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
mStreamTypes[stream].volume = value;
return NO_ERROR;
}
-status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted)
+status_t AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
{
mStreamTypes[stream].mute = muted;
return NO_ERROR;
}
-float AudioFlinger::PlaybackThread::streamVolume(int stream) const
+float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) const
{
return mStreamTypes[stream].volume;
}
-bool AudioFlinger::PlaybackThread::streamMute(int stream) const
+bool AudioFlinger::PlaybackThread::streamMute(audio_stream_type_t stream) const
{
return mStreamTypes[stream].mute;
}
@@ -1690,7 +1712,7 @@ String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
AudioSystem::OutputDescriptor desc;
- void *param2 = 0;
+ void *param2 = NULL;
ALOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param);
@@ -1720,12 +1742,12 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common);
mChannelCount = (uint16_t)popcount(mChannelMask);
mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
- mFrameSize = (uint16_t)audio_stream_frame_size(&mOutput->stream->common);
+ mFrameSize = audio_stream_frame_size(&mOutput->stream->common);
mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
// FIXME - Current mixer implementation only supports stereo output: Always
// Allocate a stereo buffer even if HW output is mono.
- if (mMixBuffer != NULL) delete[] mMixBuffer;
+ delete[] mMixBuffer;
mMixBuffer = new int16_t[mFrameCount * 2];
memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
@@ -1743,7 +1765,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
{
- if (halFrames == 0 || dspFrames == 0) {
+ if (halFrames == NULL || dspFrames == NULL) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
@@ -1793,7 +1815,7 @@ uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
}
-AudioFlinger::AudioStreamOut* AudioFlinger::PlaybackThread::getOutput()
+AudioFlinger::AudioStreamOut* AudioFlinger::PlaybackThread::getOutput() const
{
Mutex::Autolock _l(mLock);
return mOutput;
@@ -1830,13 +1852,12 @@ uint32_t AudioFlinger::PlaybackThread::activeSleepTimeUs()
// ----------------------------------------------------------------------------
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
- : PlaybackThread(audioFlinger, output, id, device),
- mAudioMixer(0), mPrevMixerStatus(MIXER_IDLE)
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ int id, uint32_t device, type_t type)
+ : PlaybackThread(audioFlinger, output, id, device, type),
+ mAudioMixer(new AudioMixer(mFrameCount, mSampleRate)),
+ mPrevMixerStatus(MIXER_IDLE)
{
- mType = ThreadBase::MIXER;
- mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
-
// FIXME - Current mixer implementation only supports stereo output
if (mChannelCount == 1) {
ALOGE("Invalid audio hardware channel count");
@@ -1851,7 +1872,7 @@ AudioFlinger::MixerThread::~MixerThread()
bool AudioFlinger::MixerThread::threadLoop()
{
Vector< sp<Track> > tracksToRemove;
- uint32_t mixerStatus = MIXER_IDLE;
+ mixer_state mixerStatus = MIXER_IDLE;
nsecs_t standbyTime = systemTime();
size_t mixBufferSize = mFrameCount * mFrameSize;
// FIXME: Relaxed timing because of a certain device that can't meet latency
@@ -1923,8 +1944,8 @@ bool AudioFlinger::MixerThread::threadLoop()
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
// put audio hardware into standby after short delay
- if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
- mSuspended) {
+ if (CC_UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
+ mSuspended)) {
if (!mStandby) {
ALOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended);
mOutput->stream->common.standby(&mOutput->stream->common);
@@ -1946,7 +1967,7 @@ bool AudioFlinger::MixerThread::threadLoop()
acquireWakeLock_l();
mPrevMixerStatus = MIXER_IDLE;
- if (mMasterMute == false) {
+ if (!mMasterMute) {
char value[PROPERTY_VALUE_MAX];
property_get("ro.audio.silent", value, "0");
if (atoi(value)) {
@@ -1968,9 +1989,9 @@ bool AudioFlinger::MixerThread::threadLoop()
// during mixing and effect process as the audio buffers could be deleted
// or modified if an effect is created or deleted
lockEffectChains_l(effectChains);
- }
+ }
- if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
+ if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
mAudioMixer->process();
// increase sleep time progressively when application underrun condition clears.
@@ -2016,11 +2037,11 @@ bool AudioFlinger::MixerThread::threadLoop()
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
- for (size_t i = 0; i < effectChains.size(); i ++) {
- effectChains[i]->process_l();
- }
- // enable changes in effect chain
- unlockEffectChains(effectChains);
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ // enable changes in effect chain
+ unlockEffectChains(effectChains);
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
@@ -2033,7 +2054,7 @@ bool AudioFlinger::MixerThread::threadLoop()
nsecs_t delta = now - mLastWriteTime;
if (!mStandby && delta > maxPeriod) {
mNumDelayedWrites++;
- if ((now - lastWarning) > kWarningThrottle) {
+ if ((now - lastWarning) > kWarningThrottleNs) {
ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
ns2ms(delta), mNumDelayedWrites, this);
lastWarning = now;
@@ -2070,10 +2091,11 @@ bool AudioFlinger::MixerThread::threadLoop()
}
// prepareTracks_l() must be called with ThreadBase::mLock held
-uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
+AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
+ const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
{
- uint32_t mixerStatus = MIXER_IDLE;
+ mixer_state mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
size_t count = activeTracks.size();
size_t mixedTracks = 0;
@@ -2098,12 +2120,13 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
sp<Track> t = activeTracks[i].promote();
if (t == 0) continue;
+ // this const just means the local variable doesn't change
Track* const track = t.get();
audio_track_cblk_t* cblk = track->cblk();
// The first time a track is added we wait
// for all its buffers to be filled before processing it
- mAudioMixer->setActiveTrack(track->name());
+ int name = track->name();
// 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:
@@ -2123,13 +2146,13 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// the minimum track buffer size is normally twice the number of frames necessary
// to fill one buffer and the resampler should not leave more than one buffer worth
// of unreleased frames after each pass, but just in case...
- LOG_ASSERT(minFrames <= cblk->frameCount);
+ ALOG_ASSERT(minFrames <= cblk->frameCount);
}
}
if ((cblk->framesReady() >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
- //ALOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this);
+ //ALOGV("track %d u=%08x, s=%08x [OK] on thread %p", name, cblk->user, cblk->server, this);
mixedTracks++;
@@ -2142,8 +2165,8 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
if (chain != 0) {
tracksWithEffect++;
} else {
- ALOGW("prepareTracks_l(): track %08x attached to effect but no chain found on session %d",
- track->name(), track->sessionId());
+ ALOGW("prepareTracks_l(): track %d attached to effect but no chain found on session %d",
+ name, track->sessionId());
}
}
@@ -2156,7 +2179,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
track->mState = TrackBase::ACTIVE;
param = AudioMixer::RAMP_VOLUME;
}
- mAudioMixer->setParameter(AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
+ mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
} else if (cblk->server != 0) {
// If the track is stopped before the first frame was mixed,
// do not apply ramp
@@ -2176,10 +2199,31 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// read original volumes with volume control
float typeVolume = mStreamTypes[track->type()].volume;
float v = masterVolume * typeVolume;
- vl = (uint32_t)(v * cblk->volume[0]) << 12;
- vr = (uint32_t)(v * cblk->volume[1]) << 12;
-
- va = (uint32_t)(v * cblk->sendLevel);
+ uint32_t vlr = cblk->getVolumeLR();
+ vl = vlr & 0xFFFF;
+ vr = vlr >> 16;
+ // track volumes come from shared memory, so can't be trusted and must be clamped
+ if (vl > MAX_GAIN_INT) {
+ ALOGV("Track left volume out of range: %04X", vl);
+ vl = MAX_GAIN_INT;
+ }
+ if (vr > MAX_GAIN_INT) {
+ ALOGV("Track right volume out of range: %04X", vr);
+ vr = MAX_GAIN_INT;
+ }
+ // now apply the master volume and stream type volume
+ vl = (uint32_t)(v * vl) << 12;
+ vr = (uint32_t)(v * vr) << 12;
+ // assuming master volume and stream type volume each go up to 1.0,
+ // vl and vr are now in 8.24 format
+
+ uint16_t sendLevel = cblk->getSendLevel_U4_12();
+ // send level comes from shared memory and so may be corrupt
+ if (sendLevel >= MAX_GAIN_INT) {
+ ALOGV("Track send level out of range: %04X", sendLevel);
+ sendLevel = MAX_GAIN_INT;
+ }
+ va = (uint32_t)(v * sendLevel);
}
// Delegate volume control to effect in track effect chain if needed
if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
@@ -2197,6 +2241,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// Convert volumes from 8.24 to 4.12 format
int16_t left, right, aux;
+ // This additional clamping is needed in case chain->setVolume_l() overshot
uint32_t v_clamped = (vl + (1 << 11)) >> 12;
if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
left = int16_t(v_clamped);
@@ -2208,26 +2253,31 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
aux = int16_t(va);
// XXX: these things DON'T need to be done each time
- mAudioMixer->setBufferProvider(track);
- mAudioMixer->enable(AudioMixer::MIXING);
+ mAudioMixer->setBufferProvider(name, track);
+ mAudioMixer->enable(name);
- mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left);
- mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right);
- mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux);
+ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)left);
+ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)right);
+ mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)aux);
mAudioMixer->setParameter(
+ name,
AudioMixer::TRACK,
AudioMixer::FORMAT, (void *)track->format());
mAudioMixer->setParameter(
+ name,
AudioMixer::TRACK,
AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
mAudioMixer->setParameter(
+ name,
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
(void *)(cblk->sampleRate));
mAudioMixer->setParameter(
+ name,
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
mAudioMixer->setParameter(
+ name,
AudioMixer::TRACK,
AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
@@ -2241,7 +2291,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
mixerStatus = MIXER_TRACKS_READY;
}
} else {
- //ALOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", track->name(), cblk->user, cblk->server, this);
+ //ALOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", name, cblk->user, cblk->server, this);
if (track->isStopped()) {
track->reset();
}
@@ -2253,7 +2303,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
if (--(track->mRetryCount) <= 0) {
- ALOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
+ ALOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this);
tracksToRemove->add(track);
// indicate to client process that the track was disabled because of underrun
android_atomic_or(CBLK_DISABLED_ON, &cblk->flags);
@@ -2265,13 +2315,13 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
mixerStatus = MIXER_TRACKS_ENABLED;
}
}
- mAudioMixer->disable(AudioMixer::MIXING);
+ mAudioMixer->disable(name);
}
}
// remove all the tracks that need to be...
count = tracksToRemove->size();
- if (UNLIKELY(count)) {
+ if (CC_UNLIKELY(count)) {
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove->itemAt(i);
mActiveTracks.remove(track);
@@ -2299,7 +2349,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
return mixerStatus;
}
-void AudioFlinger::MixerThread::invalidateTracks(int streamType)
+void AudioFlinger::MixerThread::invalidateTracks(audio_stream_type_t streamType)
{
ALOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
this, streamType, mTracks.size());
@@ -2315,7 +2365,7 @@ void AudioFlinger::MixerThread::invalidateTracks(int streamType)
}
}
-void AudioFlinger::PlaybackThread::setStreamValid(int streamType, bool valid)
+void AudioFlinger::PlaybackThread::setStreamValid(audio_stream_type_t streamType, bool valid)
{
ALOGV ("PlaybackThread::setStreamValid() thread %p, streamType %d, valid %d",
this, streamType, valid);
@@ -2352,7 +2402,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- if (value != AUDIO_FORMAT_PCM_16_BIT) {
+ if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
status = BAD_VALUE;
} else {
reconfig = true;
@@ -2367,7 +2417,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
}
if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
// do not accept frame count changes if tracks are open as the track buffer
- // size depends on frame count and correct behavior would not be garantied
+ // size depends on frame count and correct behavior would not be guaranteed
// if frame count is changed after track creation
if (!mTracks.isEmpty()) {
status = INVALID_OPERATION;
@@ -2417,6 +2467,8 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
}
if (status == NO_ERROR && reconfig) {
delete mAudioMixer;
+ // for safety in case readOutputParameters() accesses mAudioMixer (it doesn't)
+ mAudioMixer = NULL;
readOutputParameters();
mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
@@ -2438,7 +2490,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
mParamCond.signal();
// wait for condition with time out in case the thread calling ThreadBase::setParameters()
// already timed out waiting for the status and will never signal the condition.
- mWaitWorkCV.waitRelative(mLock, kSetParametersTimeout);
+ mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
}
return reconfig;
}
@@ -2469,23 +2521,16 @@ uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs()
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
- : PlaybackThread(audioFlinger, output, id, device)
+ : PlaybackThread(audioFlinger, output, id, device, DIRECT)
+ // mLeftVolFloat, mRightVolFloat
+ // mLeftVolShort, mRightVolShort
{
- mType = ThreadBase::DIRECT;
}
AudioFlinger::DirectOutputThread::~DirectOutputThread()
{
}
-
-static inline int16_t clamp16(int32_t sample)
-{
- if ((sample>>15) ^ (sample>>31))
- sample = 0x7FFF ^ (sample>>31);
- return sample;
-}
-
static inline
int32_t mul(int16_t in, int16_t v)
{
@@ -2577,7 +2622,7 @@ void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t ri
bool AudioFlinger::DirectOutputThread::threadLoop()
{
- uint32_t mixerStatus = MIXER_IDLE;
+ mixer_state mixerStatus = MIXER_IDLE;
sp<Track> trackToRemove;
sp<Track> activeTrack;
nsecs_t standbyTime = systemTime();
@@ -2615,8 +2660,8 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
}
// put audio hardware into standby after short delay
- if UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
- mSuspended) {
+ if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
+ mSuspended)) {
// wait until we have something to do...
if (!mStandby) {
ALOGV("Audio hardware entering standby, mixer %p\n", this);
@@ -2637,7 +2682,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
ALOGV("DirectOutputThread %p TID %d waking up in active mode\n", this, gettid());
acquireWakeLock_l();
- if (mMasterMute == false) {
+ if (!mMasterMute) {
char value[PROPERTY_VALUE_MAX];
property_get("ro.audio.silent", value, "0");
if (atoi(value)) {
@@ -2693,10 +2738,11 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
} else {
float typeVolume = mStreamTypes[track->type()].volume;
float v = mMasterVolume * typeVolume;
- float v_clamped = v * cblk->volume[0];
+ uint32_t vlr = cblk->getVolumeLR();
+ float v_clamped = v * (vlr & 0xFFFF);
if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
left = v_clamped/MAX_GAIN;
- v_clamped = v * cblk->volume[1];
+ v_clamped = v * (vlr >> 16);
if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
right = v_clamped/MAX_GAIN;
}
@@ -2766,7 +2812,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
}
// remove all the tracks that need to be...
- if (UNLIKELY(trackToRemove != 0)) {
+ if (CC_UNLIKELY(trackToRemove != 0)) {
mActiveTracks.remove(trackToRemove);
if (!effectChains.isEmpty()) {
ALOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
@@ -2781,7 +2827,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
lockEffectChains_l(effectChains);
}
- if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
+ if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
AudioBufferProvider::Buffer buffer;
size_t frameCount = mFrameCount;
curBuf = (int8_t *)mMixBuffer;
@@ -2789,7 +2835,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
while (frameCount) {
buffer.frameCount = frameCount;
activeTrack->getNextBuffer(&buffer);
- if (UNLIKELY(buffer.raw == 0)) {
+ if (CC_UNLIKELY(buffer.raw == NULL)) {
memset(curBuf, 0, frameCount * mFrameSize);
break;
}
@@ -2914,7 +2960,7 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
mParamCond.signal();
// wait for condition with time out in case the thread calling ThreadBase::setParameters()
// already timed out waiting for the status and will never signal the condition.
- mWaitWorkCV.waitRelative(mLock, kSetParametersTimeout);
+ mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
}
return reconfig;
}
@@ -2955,10 +3001,11 @@ uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
// ----------------------------------------------------------------------------
-AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
- : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX)
+AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
+ AudioFlinger::MixerThread* mainThread, int id)
+ : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device(), DUPLICATING),
+ mWaitTimeMs(UINT_MAX)
{
- mType = ThreadBase::DUPLICATING;
addOutputTrack(mainThread);
}
@@ -2973,7 +3020,7 @@ AudioFlinger::DuplicatingThread::~DuplicatingThread()
bool AudioFlinger::DuplicatingThread::threadLoop()
{
Vector< sp<Track> > tracksToRemove;
- uint32_t mixerStatus = MIXER_IDLE;
+ mixer_state mixerStatus = MIXER_IDLE;
nsecs_t standbyTime = systemTime();
size_t mixBufferSize = mFrameCount*mFrameSize;
SortedVector< sp<OutputTrack> > outputTracks;
@@ -3008,8 +3055,8 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
}
// put audio hardware into standby after short delay
- if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
- mSuspended) {
+ if (CC_UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
+ mSuspended)) {
if (!mStandby) {
for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->stop();
@@ -3032,7 +3079,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
acquireWakeLock_l();
mPrevMixerStatus = MIXER_IDLE;
- if (mMasterMute == false) {
+ if (!mMasterMute) {
char value[PROPERTY_VALUE_MAX];
property_get("ro.audio.silent", value, "0");
if (atoi(value)) {
@@ -3055,7 +3102,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
lockEffectChains_l(effectChains);
}
- if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
+ if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
if (outputsReady(outputTracks)) {
mAudioMixer->process();
@@ -3198,7 +3245,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
const wp<ThreadBase>& thread,
const sp<Client>& client,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
uint32_t flags,
@@ -3207,13 +3254,17 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
: RefBase(),
mThread(thread),
mClient(client),
- mCblk(0),
+ mCblk(NULL),
+ // mBuffer
+ // mBufferEnd
mFrameCount(0),
mState(IDLE),
mClientTid(-1),
mFormat(format),
mFlags(flags & ~SYSTEM_FLAGS_MASK),
mSessionId(sessionId)
+ // mChannelCount
+ // mChannelMask
{
ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
@@ -3229,7 +3280,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
mCblkMemory = client->heap()->allocate(size);
if (mCblkMemory != 0) {
mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
- if (mCblk) { // construct the shared structure in-place.
+ if (mCblk != NULL) { // construct the shared structure in-place.
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount = frameCount;
@@ -3254,7 +3305,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
}
} else {
mCblk = (audio_track_cblk_t *)(new uint8_t[size]);
- if (mCblk) { // construct the shared structure in-place.
+ // construct the shared structure in-place.
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount = frameCount;
@@ -3267,13 +3318,12 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
// written to buffer (other flags are cleared)
mCblk->flags = CBLK_UNDERRUN_ON;
mBufferEnd = (uint8_t *)mBuffer + bufferSize;
- }
}
}
AudioFlinger::ThreadBase::TrackBase::~TrackBase()
{
- if (mCblk) {
+ if (mCblk != NULL) {
mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
if (mClient == NULL) {
delete mCblk;
@@ -3281,6 +3331,7 @@ AudioFlinger::ThreadBase::TrackBase::~TrackBase()
}
mCblkMemory.clear(); // and free the shared memory
if (mClient != NULL) {
+ // Client destructor must run with AudioFlinger mutex locked
Mutex::Autolock _l(mClient->audioFlinger()->mLock);
mClient.clear();
}
@@ -3288,7 +3339,7 @@ AudioFlinger::ThreadBase::TrackBase::~TrackBase()
void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
- buffer->raw = 0;
+ buffer->raw = NULL;
mFrameCount = buffer->frameCount;
step();
buffer->frameCount = 0;
@@ -3336,17 +3387,18 @@ uint32_t AudioFlinger::ThreadBase::TrackBase::channelMask() const {
void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
audio_track_cblk_t* cblk = this->cblk();
- int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase)*cblk->frameSize;
- int8_t *bufferEnd = bufferStart + frames * cblk->frameSize;
+ size_t frameSize = cblk->frameSize;
+ int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase)*frameSize;
+ int8_t *bufferEnd = bufferStart + frames * frameSize;
// Check validity of returned pointer in case the track control block would have been corrupted.
if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd ||
- ((unsigned long)bufferStart & (unsigned long)(cblk->frameSize - 1))) {
+ ((unsigned long)bufferStart & (unsigned long)(frameSize - 1))) {
ALOGE("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \
server %d, serverBase %d, user %d, userBase %d",
bufferStart, bufferEnd, mBuffer, mBufferEnd,
cblk->server, cblk->serverBase, cblk->user, cblk->userBase);
- return 0;
+ return NULL;
}
return bufferStart;
@@ -3358,9 +3410,9 @@ void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t f
AudioFlinger::PlaybackThread::Track::Track(
const wp<ThreadBase>& thread,
const sp<Client>& client,
- int streamType,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
@@ -3380,8 +3432,6 @@ AudioFlinger::PlaybackThread::Track::Track(
if (mName < 0) {
ALOGE("no more track names available");
}
- mVolume[0] = 1.0f;
- mVolume[1] = 1.0f;
mStreamType = streamType;
// NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
// 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
@@ -3433,6 +3483,7 @@ void AudioFlinger::PlaybackThread::Track::destroy()
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
+ uint32_t vlr = mCblk->getVolumeLR();
snprintf(buffer, size, " %05d %05d %03u %03u 0x%08x %05u %04u %1d %1d %1d %05u %05u %05u 0x%08x 0x%08x 0x%08x 0x%08x\n",
mName - AudioMixer::TRACK0,
(mClient == NULL) ? getpid() : mClient->pid(),
@@ -3445,8 +3496,8 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
mMute,
mFillingUpStatus,
mCblk->sampleRate,
- mCblk->volume[0],
- mCblk->volume[1],
+ vlr & 0xFFFF,
+ vlr >> 16,
mCblk->server,
mCblk->user,
(int)mMainBuffer,
@@ -3468,7 +3519,7 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider:
framesReady = cblk->framesReady();
- if (LIKELY(framesReady)) {
+ if (CC_LIKELY(framesReady)) {
uint32_t s = cblk->server;
uint32_t bufferEnd = cblk->serverBase + cblk->frameCount;
@@ -3481,14 +3532,14 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider:
}
buffer->raw = getBuffer(s, framesReq);
- if (buffer->raw == 0) goto getNextBuffer_exit;
+ if (buffer->raw == NULL) goto getNextBuffer_exit;
buffer->frameCount = framesReq;
return NO_ERROR;
}
getNextBuffer_exit:
- buffer->raw = 0;
+ buffer->raw = NULL;
buffer->frameCount = 0;
ALOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
return NOT_ENOUGH_DATA;
@@ -3514,7 +3565,7 @@ status_t AudioFlinger::PlaybackThread::Track::start()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
- int state = mState;
+ track_state state = mState;
// here the track could be either new, or restarted
// in both cases "unstop" the track
if (mState == PAUSED) {
@@ -3555,7 +3606,7 @@ void AudioFlinger::PlaybackThread::Track::stop()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
- int state = mState;
+ track_state state = mState;
if (mState > STOPPED) {
mState = STOPPED;
// If the track is not active (PAUSED and buffers full), flush buffers
@@ -3643,12 +3694,6 @@ void AudioFlinger::PlaybackThread::Track::mute(bool muted)
mMute = muted;
}
-void AudioFlinger::PlaybackThread::Track::setVolume(float left, float right)
-{
- mVolume[0] = left;
- mVolume[1] = right;
-}
-
status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
{
status_t status = DEAD_OBJECT;
@@ -3673,7 +3718,7 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
const wp<ThreadBase>& thread,
const sp<Client>& client,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
uint32_t flags,
@@ -3717,7 +3762,7 @@ status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvi
framesAvail = cblk->framesAvailable_l();
- if (LIKELY(framesAvail)) {
+ if (CC_LIKELY(framesAvail)) {
uint32_t s = cblk->server;
uint32_t bufferEnd = cblk->serverBase + cblk->frameCount;
@@ -3729,14 +3774,14 @@ status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvi
}
buffer->raw = getBuffer(s, framesReq);
- if (buffer->raw == 0) goto getNextBuffer_exit;
+ if (buffer->raw == NULL) goto getNextBuffer_exit;
buffer->frameCount = framesReq;
return NO_ERROR;
}
getNextBuffer_exit:
- buffer->raw = 0;
+ buffer->raw = NULL;
buffer->frameCount = 0;
return NOT_ENOUGH_DATA;
}
@@ -3786,7 +3831,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
const wp<ThreadBase>& thread,
DuplicatingThread *sourceThread,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount)
: Track(thread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, NULL, 0),
@@ -3797,7 +3842,6 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
if (mCblk != NULL) {
mCblk->flags |= CBLK_DIRECTION_OUT;
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- mCblk->volume[0] = mCblk->volume[1] = 0x1000;
mOutBuffer.frameCount = 0;
playbackThread->mTracks.add(this);
ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, " \
@@ -3881,7 +3925,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
if (mOutBuffer.frameCount == 0) {
mOutBuffer.frameCount = pInBuffer->frameCount;
nsecs_t startTime = systemTime();
- if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) {
+ if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)NO_MORE_BUFFERS) {
ALOGV ("OutputTrack::write() %p thread %p no more output buffers", this, mThread.unsafe_get());
outputBufferFull = true;
break;
@@ -3970,13 +4014,13 @@ status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProv
goto start_loop_here;
while (framesAvail == 0) {
active = mActive;
- if (UNLIKELY(!active)) {
+ if (CC_UNLIKELY(!active)) {
ALOGV("Not active and NO_MORE_BUFFERS");
- return AudioTrack::NO_MORE_BUFFERS;
+ return NO_MORE_BUFFERS;
}
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
if (result != NO_ERROR) {
- return AudioTrack::NO_MORE_BUFFERS;
+ return NO_MORE_BUFFERS;
}
// read the server count again
start_loop_here:
@@ -3985,7 +4029,7 @@ status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProv
}
// if (framesAvail < framesReq) {
-// return AudioTrack::NO_MORE_BUFFERS;
+// return NO_MORE_BUFFERS;
// }
if (framesReq > framesAvail) {
@@ -4035,7 +4079,7 @@ AudioFlinger::Client::~Client()
mAudioFlinger->removeClient_l(mPid);
}
-const sp<MemoryDealer>& AudioFlinger::Client::heap() const
+sp<MemoryDealer> AudioFlinger::Client::heap() const
{
return mMemoryDealer;
}
@@ -4045,13 +4089,12 @@ const sp<MemoryDealer>& AudioFlinger::Client::heap() const
AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,
const sp<IAudioFlingerClient>& client,
pid_t pid)
- : mAudioFlinger(audioFlinger), mPid(pid), mClient(client)
+ : mAudioFlinger(audioFlinger), mPid(pid), mAudioFlingerClient(client)
{
}
AudioFlinger::NotificationClient::~NotificationClient()
{
- mClient.clear();
}
void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
@@ -4078,6 +4121,10 @@ AudioFlinger::TrackHandle::~TrackHandle() {
mTrack->destroy();
}
+sp<IMemory> AudioFlinger::TrackHandle::getCblk() const {
+ return mTrack->getCblk();
+}
+
status_t AudioFlinger::TrackHandle::start() {
return mTrack->start();
}
@@ -4098,14 +4145,6 @@ void AudioFlinger::TrackHandle::pause() {
mTrack->pause();
}
-void AudioFlinger::TrackHandle::setVolume(float left, float right) {
- mTrack->setVolume(left, right);
-}
-
-sp<IMemory> AudioFlinger::TrackHandle::getCblk() const {
- return mTrack->getCblk();
-}
-
status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId)
{
return mTrack->attachAuxEffect(EffectId);
@@ -4123,7 +4162,7 @@ sp<IAudioRecord> AudioFlinger::openRecord(
pid_t pid,
int input,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
uint32_t flags,
@@ -4212,6 +4251,10 @@ AudioFlinger::RecordHandle::~RecordHandle() {
stop();
}
+sp<IMemory> AudioFlinger::RecordHandle::getCblk() const {
+ return mRecordTrack->getCblk();
+}
+
status_t AudioFlinger::RecordHandle::start() {
ALOGV("RecordHandle::start()");
return mRecordTrack->start();
@@ -4222,10 +4265,6 @@ void AudioFlinger::RecordHandle::stop() {
mRecordTrack->stop();
}
-sp<IMemory> AudioFlinger::RecordHandle::getCblk() const {
- return mRecordTrack->getCblk();
-}
-
status_t AudioFlinger::RecordHandle::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -4240,15 +4279,16 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
uint32_t channels,
int id,
uint32_t device) :
- ThreadBase(audioFlinger, id, device),
- mInput(input), mTrack(NULL), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0)
+ ThreadBase(audioFlinger, id, device, RECORD),
+ mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+ // mRsmpInIndex and mInputBytes set by readInputParameters()
+ mReqChannelCount(popcount(channels)),
+ mReqSampleRate(sampleRate)
+ // mBytesRead is only meaningful while active, and so is cleared in start()
+ // (but might be better to also clear here for dump?)
{
- mType = ThreadBase::RECORD;
-
snprintf(mName, kNameLength, "AudioIn_%d", id);
- mReqChannelCount = popcount(channels);
- mReqSampleRate = sampleRate;
readInputParameters();
}
@@ -4256,10 +4296,8 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
AudioFlinger::RecordThread::~RecordThread()
{
delete[] mRsmpInBuffer;
- if (mResampler != 0) {
- delete mResampler;
- delete[] mRsmpOutBuffer;
- }
+ delete mResampler;
+ delete[] mRsmpOutBuffer;
}
void AudioFlinger::RecordThread::onFirstRef()
@@ -4348,9 +4386,9 @@ bool AudioFlinger::RecordThread::threadLoop()
}
buffer.frameCount = mFrameCount;
- if (LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+ if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
size_t framesOut = buffer.frameCount;
- if (mResampler == 0) {
+ if (mResampler == NULL) {
// no resampling
while (framesOut) {
size_t framesIn = mFrameCount - mRsmpInIndex;
@@ -4415,7 +4453,7 @@ bool AudioFlinger::RecordThread::threadLoop()
// ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer()
// are 32 bit aligned which should be always true.
if (mChannelCount == 2 && mReqChannelCount == 1) {
- AudioMixer::ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
+ ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
// the resampler always outputs stereo samples: do post stereo to mono conversion
int16_t *src = (int16_t *)mRsmpOutBuffer;
int16_t *dst = buffer.i16;
@@ -4424,7 +4462,7 @@ bool AudioFlinger::RecordThread::threadLoop()
src += 2;
}
} else {
- AudioMixer::ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
+ ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
}
}
@@ -4435,7 +4473,7 @@ bool AudioFlinger::RecordThread::threadLoop()
else {
if (!mActiveTrack->setOverflow()) {
nsecs_t now = systemTime();
- if ((now - lastWarning) > kWarningThrottle) {
+ if ((now - lastWarning) > kWarningThrottleNs) {
ALOGW("RecordThread: buffer overflow");
lastWarning = now;
}
@@ -4468,7 +4506,7 @@ bool AudioFlinger::RecordThread::threadLoop()
sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l(
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
- int format,
+ audio_format_t format,
int channelMask,
int frameCount,
uint32_t flags,
@@ -4517,7 +4555,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
sp <ThreadBase> strongMe = this;
status_t status = NO_ERROR;
{
- AutoMutex lock(&mLock);
+ AutoMutex lock(mLock);
if (mActiveTrack != 0) {
if (recordTrack != mActiveTrack.get()) {
status = -EBUSY;
@@ -4569,7 +4607,7 @@ void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
ALOGV("RecordThread::stop");
sp <ThreadBase> strongMe = this;
{
- AutoMutex lock(&mLock);
+ AutoMutex lock(mLock);
if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) {
mActiveTrack->mState = TrackBase::PAUSING;
// do not wait for mStartStopCond if exiting
@@ -4608,7 +4646,7 @@ status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "In size: %d\n", mInputBytes);
result.append(buffer);
- snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != 0));
+ snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != NULL));
result.append(buffer);
snprintf(buffer, SIZE, "Out channel count: %d\n", mReqChannelCount);
result.append(buffer);
@@ -4643,7 +4681,7 @@ status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer*
mInput->stream->common.standby(&mInput->stream->common);
usleep(kRecordThreadSleepUs);
}
- buffer->raw = 0;
+ buffer->raw = NULL;
buffer->frameCount = 0;
return NOT_ENOUGH_DATA;
}
@@ -4680,7 +4718,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
String8 keyValuePair = mNewParameters[0];
AudioParameter param = AudioParameter(keyValuePair);
int value;
- int reqFormat = mFormat;
+ audio_format_t reqFormat = mFormat;
int reqSamplingRate = mReqSampleRate;
int reqChannelCount = mReqChannelCount;
@@ -4689,7 +4727,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- reqFormat = value;
+ reqFormat = (audio_format_t) value;
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
@@ -4758,7 +4796,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
mParamCond.signal();
// wait for condition with time out in case the thread calling ThreadBase::setParameters()
// already timed out waiting for the status and will never signal the condition.
- mWaitWorkCV.waitRelative(mLock, kSetParametersTimeout);
+ mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
}
return reconfig;
}
@@ -4781,7 +4819,7 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
AudioSystem::OutputDescriptor desc;
- void *param2 = 0;
+ void *param2 = NULL;
switch (event) {
case AudioSystem::INPUT_OPENED:
@@ -4803,16 +4841,18 @@ void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
void AudioFlinger::RecordThread::readInputParameters()
{
- if (mRsmpInBuffer) delete mRsmpInBuffer;
- if (mRsmpOutBuffer) delete mRsmpOutBuffer;
- if (mResampler) delete mResampler;
- mResampler = 0;
+ delete mRsmpInBuffer;
+ // mRsmpInBuffer is always assigned a new[] below
+ delete mRsmpOutBuffer;
+ mRsmpOutBuffer = NULL;
+ delete mResampler;
+ mResampler = NULL;
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
mChannelCount = (uint16_t)popcount(mChannelMask);
mFormat = mInput->stream->common.get_format(&mInput->stream->common);
- mFrameSize = (uint16_t)audio_stream_frame_size(&mInput->stream->common);
+ mFrameSize = audio_stream_frame_size(&mInput->stream->common);
mInputBytes = mInput->stream->common.get_buffer_size(&mInput->stream->common);
mFrameCount = mInputBytes / mFrameSize;
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
@@ -4872,7 +4912,7 @@ AudioFlinger::RecordThread::RecordTrack* AudioFlinger::RecordThread::track()
return mTrack;
}
-AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::getInput()
+AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::getInput() const
{
Mutex::Autolock _l(mLock);
return mInput;
@@ -4900,7 +4940,7 @@ audio_stream_t* AudioFlinger::RecordThread::stream()
int AudioFlinger::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
- uint32_t *pFormat,
+ audio_format_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
uint32_t flags)
@@ -4909,7 +4949,7 @@ int AudioFlinger::openOutput(uint32_t *pDevices,
PlaybackThread *thread = NULL;
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
- uint32_t format = pFormat ? *pFormat : 0;
+ audio_format_t format = pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT;
uint32_t channels = pChannels ? *pChannels : 0;
uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
audio_stream_out_t *outStream;
@@ -4932,7 +4972,7 @@ int AudioFlinger::openOutput(uint32_t *pDevices,
if (outHwDev == NULL)
return 0;
- status = outHwDev->open_output_stream(outHwDev, *pDevices, (int *)&format,
+ status = outHwDev->open_output_stream(outHwDev, *pDevices, &format,
&channels, &samplingRate, &outStream);
ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d",
outStream,
@@ -4957,10 +4997,10 @@ int AudioFlinger::openOutput(uint32_t *pDevices,
}
mPlaybackThreads.add(id, thread);
- if (pSamplingRate) *pSamplingRate = samplingRate;
- if (pFormat) *pFormat = format;
- if (pChannels) *pChannels = channels;
- if (pLatencyMs) *pLatencyMs = thread->latency();
+ if (pSamplingRate != NULL) *pSamplingRate = samplingRate;
+ if (pFormat != NULL) *pFormat = format;
+ if (pChannels != NULL) *pChannels = channels;
+ if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
@@ -5012,7 +5052,7 @@ status_t AudioFlinger::closeOutput(int output)
}
}
}
- void *param2 = 0;
+ void *param2 = NULL;
audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
mPlaybackThreads.removeItem(output);
}
@@ -5020,6 +5060,7 @@ status_t AudioFlinger::closeOutput(int output)
if (thread->type() != ThreadBase::DUPLICATING) {
AudioStreamOut *out = thread->clearOutput();
+ assert(out != NULL);
// from now on thread->mOutput is NULL
out->hwDev->close_output_stream(out->hwDev, out->stream);
delete out;
@@ -5060,17 +5101,17 @@ status_t AudioFlinger::restoreOutput(int output)
int AudioFlinger::openInput(uint32_t *pDevices,
uint32_t *pSamplingRate,
- uint32_t *pFormat,
+ audio_format_t *pFormat,
uint32_t *pChannels,
- uint32_t acoustics)
+ audio_in_acoustics_t acoustics)
{
status_t status;
RecordThread *thread = NULL;
uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
- uint32_t format = pFormat ? *pFormat : 0;
+ audio_format_t format = pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT;
uint32_t channels = pChannels ? *pChannels : 0;
uint32_t reqSamplingRate = samplingRate;
- uint32_t reqFormat = format;
+ audio_format_t reqFormat = format;
uint32_t reqChannels = channels;
audio_stream_in_t *inStream;
audio_hw_device_t *inHwDev;
@@ -5085,9 +5126,9 @@ int AudioFlinger::openInput(uint32_t *pDevices,
if (inHwDev == NULL)
return 0;
- status = inHwDev->open_input_stream(inHwDev, *pDevices, (int *)&format,
+ status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
&channels, &samplingRate,
- (audio_in_acoustics_t)acoustics,
+ acoustics,
&inStream);
ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d",
inStream,
@@ -5105,9 +5146,9 @@ int AudioFlinger::openInput(uint32_t *pDevices,
(samplingRate <= 2 * reqSamplingRate) &&
(popcount(channels) < 3) && (popcount(reqChannels) < 3)) {
ALOGV("openInput() reopening with proposed sampling rate and channels");
- status = inHwDev->open_input_stream(inHwDev, *pDevices, (int *)&format,
+ status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
&channels, &samplingRate,
- (audio_in_acoustics_t)acoustics,
+ acoustics,
&inStream);
}
@@ -5127,9 +5168,9 @@ int AudioFlinger::openInput(uint32_t *pDevices,
device);
mRecordThreads.add(id, thread);
ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
- if (pSamplingRate) *pSamplingRate = reqSamplingRate;
- if (pFormat) *pFormat = format;
- if (pChannels) *pChannels = reqChannels;
+ if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;
+ if (pFormat != NULL) *pFormat = format;
+ if (pChannels != NULL) *pChannels = reqChannels;
input->stream->common.standby(&input->stream->common);
@@ -5154,13 +5195,14 @@ status_t AudioFlinger::closeInput(int input)
}
ALOGV("closeInput() %d", input);
- void *param2 = 0;
+ void *param2 = NULL;
audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2);
mRecordThreads.removeItem(input);
}
thread->exit();
AudioStreamIn *in = thread->clearInput();
+ assert(in != NULL);
// from now on thread->mInput is NULL
in->hwDev->close_input_stream(in->hwDev, in->stream);
delete in;
@@ -5168,7 +5210,7 @@ status_t AudioFlinger::closeInput(int input)
return NO_ERROR;
}
-status_t AudioFlinger::setStreamOutput(uint32_t stream, int output)
+status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, int output)
{
Mutex::Autolock _l(mLock);
MixerThread *dstThread = checkMixerThread_l(output);
@@ -5215,12 +5257,8 @@ void AudioFlinger::acquireAudioSessionId(int audioSession)
return;
}
}
- AudioSessionRef *ref = new AudioSessionRef();
- ref->sessionid = audioSession;
- ref->pid = caller;
- ref->cnt = 1;
- mAudioSessionRefs.push(ref);
- ALOGV(" added new entry for %d", ref->sessionid);
+ mAudioSessionRefs.push(new AudioSessionRef(audioSession, caller));
+ ALOGV(" added new entry for %d", audioSession);
}
void AudioFlinger::releaseAudioSessionId(int audioSession)
@@ -5736,7 +5774,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
effect = chain->getEffectFromDesc_l(desc);
}
- ALOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
+ ALOGV("createEffect_l() got effect %p on chain %p", effect.get(), chain.get());
if (effect == 0) {
int id = mAudioFlinger->nextUniqueId();
@@ -5764,7 +5802,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
lStatus = effect->addHandle(handle);
- if (enabled) {
+ if (enabled != NULL) {
*enabled = (int)effect->isEnabled();
}
}
@@ -5895,7 +5933,7 @@ sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain_l(int ses
return chain;
}
-void AudioFlinger::ThreadBase::setMode(uint32_t mode)
+void AudioFlinger::ThreadBase::setMode(audio_mode_t mode)
{
Mutex::Autolock _l(mLock);
size_t size = mEffectChains.size();
@@ -6151,7 +6189,7 @@ AudioFlinger::EffectModule::~EffectModule()
}
}
-status_t AudioFlinger::EffectModule::addHandle(sp<EffectHandle>& handle)
+status_t AudioFlinger::EffectModule::addHandle(const sp<EffectHandle>& handle)
{
status_t status;
@@ -6198,7 +6236,7 @@ size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle)
bool enabled = false;
EffectHandle *hdl = handle.unsafe_get();
- if (hdl) {
+ if (hdl != NULL) {
ALOGV("removeHandle() unsafe_get OK");
enabled = hdl->enabled();
}
@@ -6295,7 +6333,7 @@ void AudioFlinger::EffectModule::process()
if (isProcessEnabled()) {
// do 32 bit to 16 bit conversion for auxiliary effect input buffer
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32,
+ ditherAndClamp(mConfig.inputCfg.buffer.s32,
mConfig.inputCfg.buffer.s32,
mConfig.inputCfg.buffer.frameCount/2);
}
@@ -6400,7 +6438,7 @@ status_t AudioFlinger::EffectModule::configure()
status_t cmdStatus;
uint32_t size = sizeof(int);
status_t status = (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_CONFIGURE,
+ EFFECT_CMD_SET_CONFIG,
sizeof(effect_config_t),
&mConfig,
&size,
@@ -6693,7 +6731,7 @@ status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
return status;
}
-status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
+status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode)
{
Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
@@ -6702,7 +6740,7 @@ status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
uint32_t size = sizeof(status_t);
status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_SET_AUDIO_MODE,
- sizeof(int),
+ sizeof(audio_mode_t),
&mode,
&size,
&cmdStatus);
@@ -6718,7 +6756,8 @@ void AudioFlinger::EffectModule::setSuspended(bool suspended)
Mutex::Autolock _l(mLock);
mSuspended = suspended;
}
-bool AudioFlinger::EffectModule::suspended()
+
+bool AudioFlinger::EffectModule::suspended() const
{
Mutex::Autolock _l(mLock);
return mSuspended;
@@ -6833,7 +6872,7 @@ AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
if (mCblkMemory != 0) {
mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer());
- if (mCblk) {
+ if (mCblk != NULL) {
new(mCblk) effect_param_cblk_t();
mBuffer = (uint8_t *)mCblk + bufOffset;
}
@@ -6930,7 +6969,7 @@ void AudioFlinger::EffectHandle::disconnect(bool unpiniflast)
// release sp on module => module destructor can be called now
mEffect.clear();
if (mClient != 0) {
- if (mCblk) {
+ if (mCblk != NULL) {
mCblk->~effect_param_cblk_t(); // destroy our shared-structure.
}
mCblkMemory.clear(); // and free the shared memory
@@ -7060,7 +7099,7 @@ status_t AudioFlinger::EffectHandle::onTransact(
void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
{
- bool locked = mCblk ? tryLock(mCblk->lock) : false;
+ bool locked = mCblk != NULL && tryLock(mCblk->lock);
snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n",
(mClient == NULL) ? getpid() : mClient->pid(),
@@ -7351,7 +7390,7 @@ void AudioFlinger::EffectChain::setDevice_l(uint32_t device)
}
// setMode_l() must be called with PlaybackThread::mLock held
-void AudioFlinger::EffectChain::setMode_l(uint32_t mode)
+void AudioFlinger::EffectChain::setMode_l(audio_mode_t mode)
{
size_t size = mEffects.size();
for (size_t i = 0; i < size; i++) {
@@ -7522,7 +7561,8 @@ void AudioFlinger::EffectChain::setEffectSuspendedAll_l(bool suspend)
ALOGV("setEffectSuspendedAll_l() add entry for 0");
}
if (desc->mRefCount++ == 0) {
- Vector< sp<EffectModule> > effects = getSuspendEligibleEffects();
+ Vector< sp<EffectModule> > effects;
+ getSuspendEligibleEffects(effects);
for (size_t i = 0; i < effects.size(); i++) {
setEffectSuspended_l(&effects[i]->desc().type, true);
}
@@ -7573,16 +7613,14 @@ bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descript
return true;
}
-Vector< sp<AudioFlinger::EffectModule> > AudioFlinger::EffectChain::getSuspendEligibleEffects()
+void AudioFlinger::EffectChain::getSuspendEligibleEffects(Vector< sp<AudioFlinger::EffectModule> > &effects)
{
- Vector< sp<EffectModule> > effects;
+ effects.clear();
for (size_t i = 0; i < mEffects.size(); i++) {
- if (!isEffectEligibleForSuspend(mEffects[i]->desc())) {
- continue;
+ if (isEffectEligibleForSuspend(mEffects[i]->desc())) {
+ effects.add(mEffects[i]);
}
- effects.add(mEffects[i]);
}
- return effects;
}
sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectIfEnabled(
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 9bd2c7f..3f3188c 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -26,7 +26,7 @@
#include <media/IAudioFlingerClient.h>
#include <media/IAudioTrack.h>
#include <media/IAudioRecord.h>
-#include <media/AudioTrack.h>
+#include <media/AudioSystem.h>
#include <utils/Atomic.h>
#include <utils/Errors.h>
@@ -55,12 +55,6 @@ class AudioResampler;
// ----------------------------------------------------------------------------
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
-
-
-// ----------------------------------------------------------------------------
-
static const nsecs_t kStandbyTimeInNsecs = seconds(3);
class AudioFlinger :
@@ -69,16 +63,16 @@ class AudioFlinger :
{
friend class BinderService<AudioFlinger>;
public:
- static char const* getServiceName() { return "media.audio_flinger"; }
+ static const char* getServiceName() { return "media.audio_flinger"; }
virtual status_t dump(int fd, const Vector<String16>& args);
// IAudioFlinger interface
virtual sp<IAudioTrack> createTrack(
pid_t pid,
- int streamType,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
uint32_t flags,
@@ -89,7 +83,7 @@ public:
virtual uint32_t sampleRate(int output) const;
virtual int channelCount(int output) const;
- virtual uint32_t format(int output) const;
+ virtual audio_format_t format(int output) const;
virtual size_t frameCount(int output) const;
virtual uint32_t latency(int output) const;
@@ -99,13 +93,13 @@ public:
virtual float masterVolume() const;
virtual bool masterMute() const;
- virtual status_t setStreamVolume(int stream, float value, int output);
- virtual status_t setStreamMute(int stream, bool muted);
+ virtual status_t setStreamVolume(audio_stream_type_t stream, float value, int output);
+ virtual status_t setStreamMute(audio_stream_type_t stream, bool muted);
- virtual float streamVolume(int stream, int output) const;
- virtual bool streamMute(int stream) const;
+ virtual float streamVolume(audio_stream_type_t stream, int output) const;
+ virtual bool streamMute(audio_stream_type_t stream) const;
- virtual status_t setMode(int mode);
+ virtual status_t setMode(audio_mode_t mode);
virtual status_t setMicMute(bool state);
virtual bool getMicMute() const;
@@ -115,12 +109,12 @@ public:
virtual void registerClient(const sp<IAudioFlingerClient>& client);
- virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
+ virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount);
virtual unsigned int getInputFramesLost(int ioHandle);
virtual int openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
- uint32_t *pFormat,
+ audio_format_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
uint32_t flags);
@@ -135,13 +129,13 @@ public:
virtual int openInput(uint32_t *pDevices,
uint32_t *pSamplingRate,
- uint32_t *pFormat,
+ audio_format_t *pFormat,
uint32_t *pChannels,
- uint32_t acoustics);
+ audio_in_acoustics_t acoustics);
virtual status_t closeInput(int input);
- virtual status_t setStreamOutput(uint32_t stream, int output);
+ virtual status_t setStreamOutput(audio_stream_type_t stream, int output);
virtual status_t setVoiceVolume(float volume);
@@ -195,7 +189,7 @@ public:
pid_t pid,
int input,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
uint32_t flags,
@@ -208,11 +202,12 @@ public:
Parcel* reply,
uint32_t flags);
- uint32_t getMode() { return mMode; }
+ audio_mode_t getMode() const { return mMode; }
bool btNrecIsOff() { return mBtNrecIsOff; }
private:
+
AudioFlinger();
virtual ~AudioFlinger();
@@ -231,16 +226,16 @@ private:
public:
Client(const sp<AudioFlinger>& audioFlinger, pid_t pid);
virtual ~Client();
- const sp<MemoryDealer>& heap() const;
+ sp<MemoryDealer> heap() const;
pid_t pid() const { return mPid; }
sp<AudioFlinger> audioFlinger() { return mAudioFlinger; }
private:
Client(const Client&);
Client& operator = (const Client&);
- sp<AudioFlinger> mAudioFlinger;
- sp<MemoryDealer> mMemoryDealer;
- pid_t mPid;
+ const sp<AudioFlinger> mAudioFlinger;
+ const sp<MemoryDealer> mMemoryDealer;
+ const pid_t mPid;
};
// --- Notification Client ---
@@ -251,7 +246,7 @@ private:
pid_t pid);
virtual ~NotificationClient();
- sp<IAudioFlingerClient> client() { return mClient; }
+ sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
// IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
@@ -260,9 +255,9 @@ private:
NotificationClient(const NotificationClient&);
NotificationClient& operator = (const NotificationClient&);
- sp<AudioFlinger> mAudioFlinger;
- pid_t mPid;
- sp<IAudioFlingerClient> mClient;
+ const sp<AudioFlinger> mAudioFlinger;
+ const pid_t mPid;
+ const sp<IAudioFlingerClient> mAudioFlingerClient;
};
class TrackHandle;
@@ -282,17 +277,17 @@ private:
class ThreadBase : public Thread {
public:
- ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device);
- virtual ~ThreadBase();
-
- enum type {
+ enum type_t {
MIXER, // Thread class is MixerThread
DIRECT, // Thread class is DirectOutputThread
DUPLICATING, // Thread class is DuplicatingThread
RECORD // Thread class is RecordThread
};
+ ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device, type_t type);
+ virtual ~ThreadBase();
+
status_t dumpBase(int fd, const Vector<String16>& args);
status_t dumpEffectChains(int fd, const Vector<String16>& args);
@@ -321,7 +316,7 @@ private:
TrackBase(const wp<ThreadBase>& thread,
const sp<Client>& client,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
uint32_t flags,
@@ -349,7 +344,7 @@ private:
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
- uint32_t format() const {
+ audio_format_t format() const {
return mFormat;
}
@@ -372,19 +367,19 @@ private:
bool step();
void reset();
- wp<ThreadBase> mThread;
- sp<Client> mClient;
+ const wp<ThreadBase> mThread;
+ /*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk;
void* mBuffer;
void* mBufferEnd;
uint32_t mFrameCount;
// we don't really need a lock for these
- int mState;
+ track_state mState;
int mClientTid;
- uint32_t mFormat;
+ const audio_format_t mFormat;
uint32_t mFlags;
- int mSessionId;
+ const int mSessionId;
uint8_t mChannelCount;
uint32_t mChannelMask;
};
@@ -413,10 +408,10 @@ private:
};
virtual status_t initCheck() const = 0;
- int type() const { return mType; }
+ type_t type() const { return mType; }
uint32_t sampleRate() const;
int channelCount() const;
- uint32_t format() const;
+ audio_format_t format() const;
size_t frameCount() const;
void wakeUp() { mWaitWorkCV.broadcast(); }
void exit();
@@ -467,7 +462,7 @@ private:
// unlock effect chains after process
void unlockEffectChains(Vector<sp <EffectChain> >& effectChains);
// set audio mode to all effect chains
- void setMode(uint32_t mode);
+ void setMode(audio_mode_t mode);
// get effect module with corresponding ID on specified audio session
sp<AudioFlinger::EffectModule> getEffect_l(int sessionId, int effectId);
// add and effect module. Also creates the effect chain is none exists for
@@ -535,19 +530,19 @@ private:
friend class RecordThread;
friend class RecordTrack;
- int mType;
+ const type_t mType;
Condition mWaitWorkCV;
- sp<AudioFlinger> mAudioFlinger;
+ const sp<AudioFlinger> mAudioFlinger;
uint32_t mSampleRate;
size_t mFrameCount;
uint32_t mChannelMask;
uint16_t mChannelCount;
- uint16_t mFrameSize;
- uint32_t mFormat;
+ size_t mFrameSize;
+ audio_format_t mFormat;
Condition mParamCond;
Vector<String8> mNewParameters;
status_t mParamStatus;
- Vector<ConfigEvent *> mConfigEvents;
+ Vector<ConfigEvent> mConfigEvents;
bool mStandby;
int mId;
bool mExiting;
@@ -558,7 +553,7 @@ private:
char mName[kNameLength];
sp<IPowerManager> mPowerManager;
sp<IBinder> mWakeLockToken;
- sp<PMDeathRecipient> mDeathRecipient;
+ const sp<PMDeathRecipient> mDeathRecipient;
// list of suspended effects per session and per type. The first vector is
// keyed by session ID, the second by type UUID timeLow field
KeyedVector< int, KeyedVector< int, sp<SuspendedSessionDesc> > > mSuspendedSessions;
@@ -579,9 +574,9 @@ private:
public:
Track( const wp<ThreadBase>& thread,
const sp<Client>& client,
- int streamType,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
@@ -596,12 +591,11 @@ private:
void flush();
void destroy();
void mute(bool);
- void setVolume(float left, float right);
int name() const {
return mName;
}
- int type() const {
+ audio_stream_type_t type() const {
return mStreamType;
}
status_t attachAuxEffect(int EffectId);
@@ -639,7 +633,6 @@ private:
}
// we don't really need a lock for these
- float mVolume[2];
volatile bool mMute;
// FILLED state is used for suppressing volume ramp at begin of playing
enum {FS_FILLING, FS_FILLED, FS_ACTIVE};
@@ -647,7 +640,7 @@ private:
int8_t mRetryCount;
sp<IMemory> mSharedBuffer;
bool mResetDone;
- int mStreamType;
+ audio_stream_type_t mStreamType;
int mName;
int16_t *mMainBuffer;
int32_t *mAuxBuffer;
@@ -668,7 +661,7 @@ private:
OutputTrack( const wp<ThreadBase>& thread,
DuplicatingThread *sourceThread,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount);
~OutputTrack();
@@ -678,10 +671,14 @@ private:
bool write(int16_t* data, uint32_t frames);
bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; }
bool isActive() { return mActive; }
- wp<ThreadBase>& thread() { return mThread; }
+ const wp<ThreadBase>& thread() { return mThread; }
private:
+ enum {
+ NO_MORE_BUFFERS = 0x80000001, // same in AudioTrack.h, ok to be different value
+ };
+
status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs);
void clearBufferQueue();
@@ -691,10 +688,11 @@ private:
Vector < Buffer* > mBufferQueue;
AudioBufferProvider::Buffer mOutBuffer;
bool mActive;
- DuplicatingThread* mSourceThread;
+ DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
}; // end of OutputTrack
- PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
+ PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id,
+ uint32_t device, type_t type);
virtual ~PlaybackThread();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -703,7 +701,7 @@ private:
virtual status_t readyToRun();
virtual void onFirstRef();
- virtual status_t initCheck() const { return (mOutput == 0) ? NO_INIT : NO_ERROR; }
+ virtual status_t initCheck() const { return (mOutput == NULL) ? NO_INIT : NO_ERROR; }
virtual uint32_t latency() const;
@@ -713,30 +711,30 @@ private:
virtual float masterVolume() const;
virtual bool masterMute() const;
- virtual status_t setStreamVolume(int stream, float value);
- virtual status_t setStreamMute(int stream, bool muted);
+ virtual status_t setStreamVolume(audio_stream_type_t stream, float value);
+ virtual status_t setStreamMute(audio_stream_type_t stream, bool muted);
- virtual float streamVolume(int stream) const;
- virtual bool streamMute(int stream) const;
+ virtual float streamVolume(audio_stream_type_t stream) const;
+ virtual bool streamMute(audio_stream_type_t stream) const;
sp<Track> createTrack_l(
const sp<AudioFlinger::Client>& client,
- int streamType,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
status_t *status);
- AudioStreamOut* getOutput();
+ AudioStreamOut* getOutput() const;
AudioStreamOut* clearOutput();
virtual audio_stream_t* stream();
void suspend() { mSuspended++; }
void restore() { if (mSuspended) mSuspended--; }
- bool isSuspended() { return (mSuspended != 0); }
+ bool isSuspended() const { return (mSuspended != 0); }
virtual String8 getParameters(const String8& keys);
virtual void audioConfigChanged_l(int event, int param = 0);
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
@@ -753,7 +751,7 @@ private:
virtual uint32_t hasAudioSession(int sessionId);
virtual uint32_t getStrategyForSession_l(int sessionId);
- void setStreamValid(int streamType, bool valid);
+ void setStreamValid(audio_stream_type_t streamType, bool valid);
struct stream_type_t {
stream_type_t()
@@ -771,7 +769,9 @@ private:
int16_t* mMixBuffer;
int mSuspended;
int mBytesWritten;
+ private:
bool mMasterMute;
+ protected:
SortedVector< wp<Track> > mActiveTracks;
virtual int getTrackName_l() = 0;
@@ -803,9 +803,9 @@ private:
status_t dumpTracks(int fd, const Vector<String16>& args);
SortedVector< sp<Track> > mTracks;
- // mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
+ // mStreamTypes[] uses 1 additional stream type internally for the OutputTrack used by DuplicatingThread
stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1];
- AudioStreamOut* mOutput;
+ AudioStreamOut *mOutput;
float mMasterVolume;
nsecs_t mLastWriteTime;
int mNumWrites;
@@ -818,18 +818,19 @@ private:
MixerThread (const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
int id,
- uint32_t device);
+ uint32_t device,
+ type_t type = MIXER);
virtual ~MixerThread();
// Thread virtuals
virtual bool threadLoop();
- void invalidateTracks(int streamType);
+ void invalidateTracks(audio_stream_type_t streamType);
virtual bool checkForNewParameters_l();
virtual status_t dumpInternals(int fd, const Vector<String16>& args);
protected:
- uint32_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks,
+ mixer_state prepareTracks_l(const SortedVector< wp<Track> >& activeTracks,
Vector< sp<Track> > *tracksToRemove);
virtual int getTrackName_l();
virtual void deleteTrackName_l(int name);
@@ -837,8 +838,7 @@ private:
virtual uint32_t suspendSleepTimeUs();
AudioMixer* mAudioMixer;
- uint32_t mPrevMixerStatus; // previous status (mixer_state) returned by
- // prepareTracks_l()
+ mixer_state mPrevMixerStatus; // previous status returned by prepareTracks_l()
};
class DirectOutputThread : public PlaybackThread {
@@ -892,7 +892,7 @@ private:
PlaybackThread *checkPlaybackThread_l(int output) const;
MixerThread *checkMixerThread_l(int output) const;
RecordThread *checkRecordThread_l(int input) const;
- float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
+ float streamVolumeInternal(audio_stream_type_t stream) const { return mStreamTypes[stream].volume; }
void audioConfigChanged_l(int event, int ioHandle, void *param2);
uint32_t nextUniqueId();
@@ -909,18 +909,17 @@ private:
public:
TrackHandle(const sp<PlaybackThread::Track>& track);
virtual ~TrackHandle();
+ virtual sp<IMemory> getCblk() const;
virtual status_t start();
virtual void stop();
virtual void flush();
virtual void mute(bool);
virtual void pause();
- virtual void setVolume(float left, float right);
- virtual sp<IMemory> getCblk() const;
virtual status_t attachAuxEffect(int effectId);
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
- sp<PlaybackThread::Track> mTrack;
+ const sp<PlaybackThread::Track> mTrack;
};
friend class Client;
@@ -942,7 +941,7 @@ private:
RecordTrack(const wp<ThreadBase>& thread,
const sp<Client>& client,
uint32_t sampleRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channelMask,
int frameCount,
uint32_t flags,
@@ -982,11 +981,11 @@ private:
virtual status_t readyToRun();
virtual void onFirstRef();
- virtual status_t initCheck() const { return (mInput == 0) ? NO_INIT : NO_ERROR; }
+ virtual status_t initCheck() const { return (mInput == NULL) ? NO_INIT : NO_ERROR; }
sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l(
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
- int format,
+ audio_format_t format,
int channelMask,
int frameCount,
uint32_t flags,
@@ -996,7 +995,7 @@ private:
status_t start(RecordTrack* recordTrack);
void stop(RecordTrack* recordTrack);
status_t dump(int fd, const Vector<String16>& args);
- AudioStreamIn* getInput();
+ AudioStreamIn* getInput() const;
AudioStreamIn* clearInput();
virtual audio_stream_t* stream();
@@ -1024,8 +1023,8 @@ private:
int16_t *mRsmpInBuffer;
size_t mRsmpInIndex;
size_t mInputBytes;
- int mReqChannelCount;
- uint32_t mReqSampleRate;
+ const int mReqChannelCount;
+ const uint32_t mReqSampleRate;
ssize_t mBytesRead;
};
@@ -1033,13 +1032,13 @@ private:
public:
RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack);
virtual ~RecordHandle();
+ virtual sp<IMemory> getCblk() const;
virtual status_t start();
virtual void stop();
- virtual sp<IMemory> getCblk() const;
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
- sp<RecordThread::RecordTrack> mRecordTrack;
+ const sp<RecordThread::RecordTrack> mRecordTrack;
};
//--- Audio Effect Management
@@ -1089,7 +1088,7 @@ private:
void reset_l();
status_t configure();
status_t init();
- uint32_t state() {
+ effect_state state() const {
return mState;
}
uint32_t status() {
@@ -1108,9 +1107,9 @@ private:
int16_t *outBuffer() { return mConfig.outputCfg.buffer.s16; }
void setChain(const wp<EffectChain>& chain) { mChain = chain; }
void setThread(const wp<ThreadBase>& thread) { mThread = thread; }
- wp<ThreadBase>& thread() { return mThread; }
+ const wp<ThreadBase>& thread() { return mThread; }
- status_t addHandle(sp<EffectHandle>& handle);
+ status_t addHandle(const sp<EffectHandle>& handle);
void disconnect(const wp<EffectHandle>& handle, bool unpiniflast);
size_t removeHandle (const wp<EffectHandle>& handle);
@@ -1119,11 +1118,11 @@ private:
status_t setDevice(uint32_t device);
status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
- status_t setMode(uint32_t mode);
+ status_t setMode(audio_mode_t mode);
status_t start();
status_t stop();
void setSuspended(bool suspended);
- bool suspended();
+ bool suspended() const;
sp<EffectHandle> controlHandle();
@@ -1146,7 +1145,7 @@ private:
status_t start_l();
status_t stop_l();
- Mutex mLock; // mutex for process, commands and handles list protection
+mutable Mutex mLock; // mutex for process, commands and handles list protection
wp<ThreadBase> mThread; // parent thread
wp<EffectChain> mChain; // parent effect chain
int mId; // this instance unique ID
@@ -1154,8 +1153,8 @@ private:
effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
effect_config_t mConfig; // input and output audio configuration
effect_handle_t mEffectInterface; // Effect module C API
- status_t mStatus; // initialization status
- uint32_t mState; // current activation state (effect_state)
+ status_t mStatus; // initialization status
+ effect_state mState; // current activation state
Vector< wp<EffectHandle> > mHandles; // list of client handles
uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after
// sending disable command.
@@ -1272,7 +1271,7 @@ private:
sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);
bool setVolume_l(uint32_t *left, uint32_t *right);
void setDevice_l(uint32_t device);
- void setMode_l(uint32_t mode);
+ void setMode_l(audio_mode_t mode);
void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
mInBuffer = buffer;
@@ -1328,7 +1327,8 @@ private:
// get a list of effect modules to suspend when an effect of the type
// passed is enabled.
- Vector< sp<EffectModule> > getSuspendEligibleEffects();
+ void getSuspendEligibleEffects(Vector< sp<EffectModule> > &effects);
+
// get an effect module if it is currently enable
sp<EffectModule> getEffectIfEnabled(const effect_uuid_t *type);
// true if the effect whose descriptor is passed can be suspended
@@ -1359,25 +1359,32 @@ private:
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
};
+ // AudioStreamOut and AudioStreamIn are immutable, so their fields are const.
+ // For emphasis, we could also make all pointers to them be "const *",
+ // but that would clutter the code unnecessarily.
+
struct AudioStreamOut {
- audio_hw_device_t *hwDev;
- audio_stream_out_t *stream;
+ audio_hw_device_t* const hwDev;
+ audio_stream_out_t* const stream;
AudioStreamOut(audio_hw_device_t *dev, audio_stream_out_t *out) :
hwDev(dev), stream(out) {}
};
struct AudioStreamIn {
- audio_hw_device_t *hwDev;
- audio_stream_in_t *stream;
+ audio_hw_device_t* const hwDev;
+ audio_stream_in_t* const stream;
AudioStreamIn(audio_hw_device_t *dev, audio_stream_in_t *in) :
hwDev(dev), stream(in) {}
};
struct AudioSessionRef {
- int sessionid;
- pid_t pid;
+ // FIXME rename parameter names when fields get "m" prefix
+ AudioSessionRef(int sessionid_, pid_t pid_) :
+ sessionid(sessionid_), pid(pid_), cnt(1) {}
+ const int sessionid;
+ const pid_t pid;
int cnt;
};
@@ -1391,11 +1398,13 @@ private:
mutable Mutex mHardwareLock;
audio_hw_device_t* mPrimaryHardwareDev;
Vector<audio_hw_device_t*> mAudioHwDevs;
- mutable int mHardwareStatus;
+ mutable hardware_call_state mHardwareStatus; // for dump only
DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads;
PlaybackThread::stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
+
+ // both are protected by mLock
float mMasterVolume;
bool mMasterMute;
@@ -1403,10 +1412,13 @@ private:
DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients;
volatile int32_t mNextUniqueId;
- uint32_t mMode;
+ audio_mode_t mMode;
bool mBtNrecIsOff;
Vector<AudioSessionRef*> mAudioSessionRefs;
+
+ float masterVolume_l() const { return mMasterVolume; }
+ bool masterMute_l() const { return mMasterMute; }
};
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 9dda256..0b9f8ba 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "AudioMixer"
//#define LOG_NDEBUG 0
+#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
@@ -27,240 +28,229 @@
#include <utils/Log.h>
#include <cutils/bitops.h>
+#include <cutils/compiler.h>
#include <system/audio.h>
+#include <audio_utils/primitives.h>
+
#include "AudioMixer.h"
namespace android {
-// ----------------------------------------------------------------------------
-
-static inline int16_t clamp16(int32_t sample)
-{
- if ((sample>>15) ^ (sample>>31))
- sample = 0x7FFF ^ (sample>>31);
- return sample;
-}
// ----------------------------------------------------------------------------
AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate)
- : mActiveTrack(0), mTrackNames(0), mSampleRate(sampleRate)
+ : mTrackNames(0), mSampleRate(sampleRate)
{
+ // AudioMixer is not yet capable of multi-channel beyond stereo
+ assert(2 == MAX_NUM_CHANNELS);
mState.enabledTracks= 0;
mState.needsChanged = 0;
mState.frameCount = frameCount;
- mState.outputTemp = 0;
- mState.resampleTemp = 0;
mState.hook = process__nop;
+ mState.outputTemp = NULL;
+ mState.resampleTemp = NULL;
+ // mState.reserved
track_t* t = mState.tracks;
- for (int i=0 ; i<32 ; i++) {
+ for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
t->needs = 0;
t->volume[0] = UNITY_GAIN;
t->volume[1] = UNITY_GAIN;
+ // no initialization needed
+ // t->prevVolume[0]
+ // t->prevVolume[1]
t->volumeInc[0] = 0;
t->volumeInc[1] = 0;
t->auxLevel = 0;
t->auxInc = 0;
+ // no initialization needed
+ // t->prevAuxLevel
+ // t->frameCount
t->channelCount = 2;
t->enabled = 0;
t->format = 16;
t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
- t->buffer.raw = 0;
- t->bufferProvider = 0;
- t->hook = 0;
- t->resampler = 0;
+ t->bufferProvider = NULL;
+ t->buffer.raw = NULL;
+ // t->buffer.frameCount
+ t->hook = NULL;
+ t->in = NULL;
+ t->resampler = NULL;
t->sampleRate = mSampleRate;
- t->in = 0;
t->mainBuffer = NULL;
t->auxBuffer = NULL;
t++;
}
}
- AudioMixer::~AudioMixer()
- {
- track_t* t = mState.tracks;
- for (int i=0 ; i<32 ; i++) {
- delete t->resampler;
- t++;
- }
- delete [] mState.outputTemp;
- delete [] mState.resampleTemp;
- }
-
- int AudioMixer::getTrackName()
- {
- uint32_t names = mTrackNames;
- uint32_t mask = 1;
- int n = 0;
- while (names & mask) {
- mask <<= 1;
- n++;
+AudioMixer::~AudioMixer()
+{
+ track_t* t = mState.tracks;
+ for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
+ delete t->resampler;
+ t++;
}
- if (mask) {
+ delete [] mState.outputTemp;
+ delete [] mState.resampleTemp;
+}
+
+int AudioMixer::getTrackName()
+{
+ uint32_t names = ~mTrackNames;
+ if (names != 0) {
+ int n = __builtin_ctz(names);
ALOGV("add track (%d)", n);
- mTrackNames |= mask;
+ mTrackNames |= 1 << n;
return TRACK0 + n;
}
return -1;
- }
+}
- void AudioMixer::invalidateState(uint32_t mask)
- {
+void AudioMixer::invalidateState(uint32_t mask)
+{
if (mask) {
mState.needsChanged |= mask;
mState.hook = process__validate;
}
}
- void AudioMixer::deleteTrackName(int name)
- {
+void AudioMixer::deleteTrackName(int name)
+{
name -= TRACK0;
- if (uint32_t(name) < MAX_NUM_TRACKS) {
- ALOGV("deleteTrackName(%d)", name);
- track_t& track(mState.tracks[ name ]);
- if (track.enabled != 0) {
- track.enabled = 0;
- invalidateState(1<<name);
- }
- if (track.resampler) {
- // delete the resampler
- delete track.resampler;
- track.resampler = 0;
- track.sampleRate = mSampleRate;
- invalidateState(1<<name);
- }
- track.volumeInc[0] = 0;
- track.volumeInc[1] = 0;
- mTrackNames &= ~(1<<name);
+ assert(uint32_t(name) < MAX_NUM_TRACKS);
+ ALOGV("deleteTrackName(%d)", name);
+ track_t& track(mState.tracks[ name ]);
+ if (track.enabled != 0) {
+ track.enabled = 0;
+ invalidateState(1<<name);
}
- }
-
-status_t AudioMixer::enable(int name)
-{
- switch (name) {
- case MIXING: {
- if (mState.tracks[ mActiveTrack ].enabled != 1) {
- mState.tracks[ mActiveTrack ].enabled = 1;
- ALOGV("enable(%d)", mActiveTrack);
- invalidateState(1<<mActiveTrack);
- }
- } break;
- default:
- return NAME_NOT_FOUND;
+ if (track.resampler != NULL) {
+ // delete the resampler
+ delete track.resampler;
+ track.resampler = NULL;
+ track.sampleRate = mSampleRate;
+ invalidateState(1<<name);
}
- return NO_ERROR;
+ track.volumeInc[0] = 0;
+ track.volumeInc[1] = 0;
+ mTrackNames &= ~(1<<name);
}
-status_t AudioMixer::disable(int name)
+void AudioMixer::enable(int name)
{
- switch (name) {
- case MIXING: {
- if (mState.tracks[ mActiveTrack ].enabled != 0) {
- mState.tracks[ mActiveTrack ].enabled = 0;
- ALOGV("disable(%d)", mActiveTrack);
- invalidateState(1<<mActiveTrack);
- }
- } break;
- default:
- return NAME_NOT_FOUND;
+ name -= TRACK0;
+ assert(uint32_t(name) < MAX_NUM_TRACKS);
+ track_t& track = mState.tracks[name];
+
+ if (track.enabled != 1) {
+ track.enabled = 1;
+ ALOGV("enable(%d)", name);
+ invalidateState(1 << name);
}
- return NO_ERROR;
}
-status_t AudioMixer::setActiveTrack(int track)
+void AudioMixer::disable(int name)
{
- if (uint32_t(track-TRACK0) >= MAX_NUM_TRACKS) {
- return BAD_VALUE;
+ name -= TRACK0;
+ assert(uint32_t(name) < MAX_NUM_TRACKS);
+ track_t& track = mState.tracks[name];
+
+ if (track.enabled != 0) {
+ track.enabled = 0;
+ ALOGV("disable(%d)", name);
+ invalidateState(1 << name);
}
- mActiveTrack = track - TRACK0;
- return NO_ERROR;
}
-status_t AudioMixer::setParameter(int target, int name, void *value)
+void AudioMixer::setParameter(int name, int target, int param, void *value)
{
+ name -= TRACK0;
+ assert(uint32_t(name) < MAX_NUM_TRACKS);
+ track_t& track = mState.tracks[name];
+
int valueInt = (int)value;
int32_t *valueBuf = (int32_t *)value;
switch (target) {
+
case TRACK:
- if (name == CHANNEL_MASK) {
+ switch (param) {
+ case CHANNEL_MASK: {
uint32_t mask = (uint32_t)value;
- if (mState.tracks[ mActiveTrack ].channelMask != mask) {
+ if (track.channelMask != mask) {
uint8_t channelCount = popcount(mask);
- if ((channelCount <= MAX_NUM_CHANNELS) && (channelCount)) {
- mState.tracks[ mActiveTrack ].channelMask = mask;
- mState.tracks[ mActiveTrack ].channelCount = channelCount;
- ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask);
- invalidateState(1<<mActiveTrack);
- return NO_ERROR;
- }
- } else {
- return NO_ERROR;
+ assert((channelCount <= MAX_NUM_CHANNELS) && (channelCount));
+ track.channelMask = mask;
+ track.channelCount = channelCount;
+ ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask);
+ invalidateState(1 << name);
}
- }
- if (name == MAIN_BUFFER) {
- if (mState.tracks[ mActiveTrack ].mainBuffer != valueBuf) {
- mState.tracks[ mActiveTrack ].mainBuffer = valueBuf;
+ } break;
+ case MAIN_BUFFER:
+ if (track.mainBuffer != valueBuf) {
+ track.mainBuffer = valueBuf;
ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
- invalidateState(1<<mActiveTrack);
+ invalidateState(1 << name);
}
- return NO_ERROR;
- }
- if (name == AUX_BUFFER) {
- if (mState.tracks[ mActiveTrack ].auxBuffer != valueBuf) {
- mState.tracks[ mActiveTrack ].auxBuffer = valueBuf;
+ break;
+ case AUX_BUFFER:
+ if (track.auxBuffer != valueBuf) {
+ track.auxBuffer = valueBuf;
ALOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
- invalidateState(1<<mActiveTrack);
+ invalidateState(1 << name);
}
- return NO_ERROR;
+ break;
+ default:
+ // bad param
+ assert(false);
}
-
break;
+
case RESAMPLE:
- if (name == SAMPLE_RATE) {
- if (valueInt > 0) {
- track_t& track = mState.tracks[ mActiveTrack ];
- if (track.setResampler(uint32_t(valueInt), mSampleRate)) {
- ALOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
- uint32_t(valueInt));
- invalidateState(1<<mActiveTrack);
- }
- return NO_ERROR;
+ switch (param) {
+ case SAMPLE_RATE:
+ assert(valueInt > 0);
+ if (track.setResampler(uint32_t(valueInt), mSampleRate)) {
+ ALOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
+ uint32_t(valueInt));
+ invalidateState(1 << name);
}
- }
- if (name == RESET) {
- track_t& track = mState.tracks[ mActiveTrack ];
+ break;
+ case RESET:
track.resetResampler();
- invalidateState(1<<mActiveTrack);
- return NO_ERROR;
+ invalidateState(1 << name);
+ break;
+ default:
+ // bad param
+ assert(false);
}
break;
+
case RAMP_VOLUME:
case VOLUME:
- if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) {
- track_t& track = mState.tracks[ mActiveTrack ];
- if (track.volume[name-VOLUME0] != valueInt) {
+ switch (param) {
+ case VOLUME0:
+ case VOLUME1:
+ if (track.volume[param-VOLUME0] != valueInt) {
ALOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
- track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16;
- track.volume[name-VOLUME0] = valueInt;
+ track.prevVolume[param-VOLUME0] = track.volume[param-VOLUME0] << 16;
+ track.volume[param-VOLUME0] = valueInt;
if (target == VOLUME) {
- track.prevVolume[name-VOLUME0] = valueInt << 16;
- track.volumeInc[name-VOLUME0] = 0;
+ track.prevVolume[param-VOLUME0] = valueInt << 16;
+ track.volumeInc[param-VOLUME0] = 0;
} else {
- int32_t d = (valueInt<<16) - track.prevVolume[name-VOLUME0];
+ int32_t d = (valueInt<<16) - track.prevVolume[param-VOLUME0];
int32_t volInc = d / int32_t(mState.frameCount);
- track.volumeInc[name-VOLUME0] = volInc;
+ track.volumeInc[param-VOLUME0] = volInc;
if (volInc == 0) {
- track.prevVolume[name-VOLUME0] = valueInt << 16;
+ track.prevVolume[param-VOLUME0] = valueInt << 16;
}
}
- invalidateState(1<<mActiveTrack);
+ invalidateState(1 << name);
}
- return NO_ERROR;
- } else if (name == AUXLEVEL) {
- track_t& track = mState.tracks[ mActiveTrack ];
+ break;
+ case AUXLEVEL:
if (track.auxLevel != valueInt) {
ALOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
track.prevAuxLevel = track.auxLevel << 16;
@@ -276,13 +266,19 @@ status_t AudioMixer::setParameter(int target, int name, void *value)
track.prevAuxLevel = valueInt << 16;
}
}
- invalidateState(1<<mActiveTrack);
+ invalidateState(1 << name);
}
- return NO_ERROR;
+ break;
+ default:
+ // bad param
+ assert(false);
}
break;
+
+ default:
+ // bad target
+ assert(false);
}
- return BAD_VALUE;
}
bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
@@ -290,7 +286,7 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
if (value!=devSampleRate || resampler) {
if (sampleRate != value) {
sampleRate = value;
- if (resampler == 0) {
+ if (resampler == NULL) {
resampler = AudioResampler::create(
format, channelCount, devSampleRate);
}
@@ -302,12 +298,12 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
bool AudioMixer::track_t::doesResample() const
{
- return resampler != 0;
+ return resampler != NULL;
}
void AudioMixer::track_t::resetResampler()
{
- if (resampler != 0) {
+ if (resampler != NULL) {
resampler->reset();
}
}
@@ -315,7 +311,7 @@ void AudioMixer::track_t::resetResampler()
inline
void AudioMixer::track_t::adjustVolumeRamp(bool aux)
{
- for (int i=0 ; i<2 ; i++) {
+ for (uint32_t i=0 ; i<MAX_NUM_CHANNELS ; i++) {
if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) {
volumeInc[i] = 0;
@@ -349,10 +345,11 @@ size_t AudioMixer::getUnreleasedFrames(int name)
return 0;
}
-status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer)
+void AudioMixer::setBufferProvider(int name, AudioBufferProvider* buffer)
{
- mState.tracks[ mActiveTrack ].bufferProvider = buffer;
- return NO_ERROR;
+ name -= TRACK0;
+ assert(uint32_t(name) < MAX_NUM_TRACKS);
+ mState.tracks[name].bufferProvider = buffer;
}
@@ -447,11 +444,11 @@ void AudioMixer::process__validate(state_t* state)
} else {
if (state->outputTemp) {
delete [] state->outputTemp;
- state->outputTemp = 0;
+ state->outputTemp = NULL;
}
if (state->resampleTemp) {
delete [] state->resampleTemp;
- state->resampleTemp = 0;
+ state->resampleTemp = NULL;
}
state->hook = process__genericNoResampling;
if (all16BitsStereoNoResample && !volumeRamp) {
@@ -467,115 +464,33 @@ void AudioMixer::process__validate(state_t* state)
countActiveTracks, state->enabledTracks,
all16BitsStereoNoResample, resampling, volumeRamp);
- state->hook(state);
-
- // Now that the volume ramp has been done, set optimal state and
- // track hooks for subsequent mixer process
- if (countActiveTracks) {
- int allMuted = 1;
- uint32_t en = state->enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
- track_t& t = state->tracks[i];
- if (!t.doesResample() && t.volumeRL == 0)
- {
- t.needs |= NEEDS_MUTE_ENABLED;
- t.hook = track__nop;
- } else {
- allMuted = 0;
- }
- }
- if (allMuted) {
- state->hook = process__nop;
- } else if (all16BitsStereoNoResample) {
- if (countActiveTracks == 1) {
- state->hook = process__OneTrack16BitsStereoNoResampling;
- }
- }
- }
-}
-
-static inline
-int32_t mulAdd(int16_t in, int16_t v, int32_t a)
-{
-#if defined(__arm__) && !defined(__thumb__)
- int32_t out;
- asm( "smlabb %[out], %[in], %[v], %[a] \n"
- : [out]"=r"(out)
- : [in]"%r"(in), [v]"r"(v), [a]"r"(a)
- : );
- return out;
-#else
- return a + in * int32_t(v);
-#endif
-}
-
-static inline
-int32_t mul(int16_t in, int16_t v)
-{
-#if defined(__arm__) && !defined(__thumb__)
- int32_t out;
- asm( "smulbb %[out], %[in], %[v] \n"
- : [out]"=r"(out)
- : [in]"%r"(in), [v]"r"(v)
- : );
- return out;
-#else
- return in * int32_t(v);
-#endif
-}
+ state->hook(state);
-static inline
-int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a)
-{
-#if defined(__arm__) && !defined(__thumb__)
- int32_t out;
- if (left) {
- asm( "smlabb %[out], %[inRL], %[vRL], %[a] \n"
- : [out]"=r"(out)
- : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a)
- : );
- } else {
- asm( "smlatt %[out], %[inRL], %[vRL], %[a] \n"
- : [out]"=r"(out)
- : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a)
- : );
- }
- return out;
-#else
- if (left) {
- return a + int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF);
- } else {
- return a + int16_t(inRL>>16) * int16_t(vRL>>16);
- }
-#endif
-}
-
-static inline
-int32_t mulRL(int left, uint32_t inRL, uint32_t vRL)
-{
-#if defined(__arm__) && !defined(__thumb__)
- int32_t out;
- if (left) {
- asm( "smulbb %[out], %[inRL], %[vRL] \n"
- : [out]"=r"(out)
- : [inRL]"%r"(inRL), [vRL]"r"(vRL)
- : );
- } else {
- asm( "smultt %[out], %[inRL], %[vRL] \n"
- : [out]"=r"(out)
- : [inRL]"%r"(inRL), [vRL]"r"(vRL)
- : );
- }
- return out;
-#else
- if (left) {
- return int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF);
- } else {
- return int16_t(inRL>>16) * int16_t(vRL>>16);
+ // Now that the volume ramp has been done, set optimal state and
+ // track hooks for subsequent mixer process
+ if (countActiveTracks) {
+ int allMuted = 1;
+ uint32_t en = state->enabledTracks;
+ while (en) {
+ const int i = 31 - __builtin_clz(en);
+ en &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ if (!t.doesResample() && t.volumeRL == 0)
+ {
+ t.needs |= NEEDS_MUTE_ENABLED;
+ t.hook = track__nop;
+ } else {
+ allMuted = 0;
+ }
+ }
+ if (allMuted) {
+ state->hook = process__nop;
+ } else if (all16BitsStereoNoResample) {
+ if (countActiveTracks == 1) {
+ state->hook = process__OneTrack16BitsStereoNoResampling;
+ }
+ }
}
-#endif
}
@@ -591,13 +506,13 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+ if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
volumeRampStereo(t, out, outFrameCount, temp, aux);
} else {
volumeStereo(t, out, outFrameCount, temp, aux);
}
} else {
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
@@ -628,7 +543,7 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i
// (vl + vlInc*frameCount)/65536.0f, frameCount);
// ramp volume
- if UNLIKELY(aux != NULL) {
+ if (CC_UNLIKELY(aux != NULL)) {
int32_t va = t->prevAuxLevel;
const int32_t vaInc = t->auxInc;
int32_t l;
@@ -663,7 +578,7 @@ void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32
const int16_t vl = t->volume[0];
const int16_t vr = t->volume[1];
- if UNLIKELY(aux != NULL) {
+ if (CC_UNLIKELY(aux != NULL)) {
const int16_t va = (int16_t)t->auxLevel;
do {
int16_t l = (int16_t)(*temp++ >> 12);
@@ -688,13 +603,13 @@ void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32
void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
- int16_t const *in = static_cast<int16_t const *>(t->in);
+ const int16_t *in = static_cast<const int16_t *>(t->in);
- if UNLIKELY(aux != NULL) {
+ if (CC_UNLIKELY(aux != NULL)) {
int32_t l;
int32_t r;
// ramp gain
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+ if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
int32_t vl = t->prevVolume[0];
int32_t vr = t->prevVolume[1];
int32_t va = t->prevAuxLevel;
@@ -727,7 +642,7 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount
const uint32_t vrl = t->volumeRL;
const int16_t va = (int16_t)t->auxLevel;
do {
- uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1);
in += 2;
out[0] = mulAddRL(1, rl, vrl, out[0]);
@@ -739,7 +654,7 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount
}
} else {
// ramp gain
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
int32_t vl = t->prevVolume[0];
int32_t vr = t->prevVolume[1];
const int32_t vlInc = t->volumeInc[0];
@@ -765,7 +680,7 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount
else {
const uint32_t vrl = t->volumeRL;
do {
- uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
in += 2;
out[0] = mulAddRL(1, rl, vrl, out[0]);
out[1] = mulAddRL(0, rl, vrl, out[1]);
@@ -778,11 +693,11 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount
void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
- int16_t const *in = static_cast<int16_t const *>(t->in);
+ const int16_t *in = static_cast<int16_t const *>(t->in);
- if UNLIKELY(aux != NULL) {
+ if (CC_UNLIKELY(aux != NULL)) {
// ramp gain
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
+ if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
int32_t vl = t->prevVolume[0];
int32_t vr = t->prevVolume[1];
int32_t va = t->prevAuxLevel;
@@ -825,7 +740,7 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
}
} else {
// ramp gain
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
int32_t vl = t->prevVolume[0];
int32_t vr = t->prevVolume[1];
const int32_t vlInc = t->volumeInc[0];
@@ -862,19 +777,6 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
t->in = in;
}
-void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c)
-{
- for (size_t i=0 ; i<c ; i++) {
- int32_t l = *sums++;
- int32_t r = *sums++;
- int32_t nl = l >> 12;
- int32_t nr = r >> 12;
- l = clamp16(nl);
- r = clamp16(nr);
- *out++ = (r<<16) | (l & 0xFFFF);
- }
-}
-
// no-op case
void AudioMixer::process__nop(state_t* state)
{
@@ -891,7 +793,7 @@ void AudioMixer::process__nop(state_t* state)
i = 31 - __builtin_clz(e2);
e2 &= ~(1<<i);
track_t& t2 = state->tracks[i];
- if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) {
e1 &= ~(1<<i);
}
}
@@ -907,7 +809,7 @@ void AudioMixer::process__nop(state_t* state)
while (outFrames) {
t1.buffer.frameCount = outFrames;
t1.bufferProvider->getNextBuffer(&t1.buffer);
- if (!t1.buffer.raw) break;
+ if (t1.buffer.raw == NULL) break;
outFrames -= t1.buffer.frameCount;
t1.bufferProvider->releaseBuffer(&t1.buffer);
}
@@ -949,7 +851,7 @@ void AudioMixer::process__genericNoResampling(state_t* state)
j = 31 - __builtin_clz(e2);
e2 &= ~(1<<j);
track_t& t2 = state->tracks[j];
- if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) {
e1 &= ~(1<<j);
}
}
@@ -966,7 +868,7 @@ void AudioMixer::process__genericNoResampling(state_t* state)
track_t& t = state->tracks[i];
size_t outFrames = BLOCKSIZE;
int32_t *aux = NULL;
- if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ if (CC_UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED)) {
aux = t.auxBuffer + numFrames;
}
while (outFrames) {
@@ -975,7 +877,7 @@ void AudioMixer::process__genericNoResampling(state_t* state)
(t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux);
t.frameCount -= inFrames;
outFrames -= inFrames;
- if UNLIKELY(aux != NULL) {
+ if (CC_UNLIKELY(aux != NULL)) {
aux += inFrames;
}
}
@@ -1010,9 +912,10 @@ void AudioMixer::process__genericNoResampling(state_t* state)
}
- // generic code with resampling
+// generic code with resampling
void AudioMixer::process__genericResampling(state_t* state)
{
+ // this const just means that local variable outTemp doesn't change
int32_t* const outTemp = state->outputTemp;
const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
@@ -1030,7 +933,7 @@ void AudioMixer::process__genericResampling(state_t* state)
j = 31 - __builtin_clz(e2);
e2 &= ~(1<<j);
track_t& t2 = state->tracks[j];
- if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) {
e1 &= ~(1<<j);
}
}
@@ -1042,7 +945,7 @@ void AudioMixer::process__genericResampling(state_t* state)
e1 &= ~(1<<i);
track_t& t = state->tracks[i];
int32_t *aux = NULL;
- if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ if (CC_UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED)) {
aux = t.auxBuffer;
}
@@ -1063,7 +966,7 @@ void AudioMixer::process__genericResampling(state_t* state)
// been enabled for mixing.
if (t.in == NULL) break;
- if UNLIKELY(aux != NULL) {
+ if (CC_UNLIKELY(aux != NULL)) {
aux += outFrames;
}
(t.hook)(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux);
@@ -1093,7 +996,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
while (numFrames) {
b.frameCount = numFrames;
t.bufferProvider->getNextBuffer(&b);
- int16_t const *in = b.i16;
+ const int16_t *in = b.i16;
// in == NULL can happen if the track was flushed just after having
// been enabled for mixing.
@@ -1105,11 +1008,11 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
}
size_t outFrames = b.frameCount;
- if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
+ if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
// volume is boosted, so we might need to clamp even though
// we process only one track.
do {
- uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
in += 2;
int32_t l = mulRL(1, rl, vrl) >> 12;
int32_t r = mulRL(0, rl, vrl) >> 12;
@@ -1120,7 +1023,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
} while (--outFrames);
} else {
do {
- uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
in += 2;
int32_t l = mulRL(1, rl, vrl) >> 12;
int32_t r = mulRL(0, rl, vrl) >> 12;
@@ -1132,6 +1035,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
}
}
+#if 0
// 2 tracks is also a common case
// NEVER used in current implementation of process__validate()
// only use if the 2 tracks have the same output buffer
@@ -1149,12 +1053,12 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
const track_t& t1 = state->tracks[i];
AudioBufferProvider::Buffer& b1(t1.buffer);
- int16_t const *in0;
+ const int16_t *in0;
const int16_t vl0 = t0.volume[0];
const int16_t vr0 = t0.volume[1];
size_t frameCount0 = 0;
- int16_t const *in1;
+ const int16_t *in1;
const int16_t vl1 = t1.volume[0];
const int16_t vr1 = t1.volume[1];
size_t frameCount1 = 0;
@@ -1162,7 +1066,7 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
//FIXME: only works if two tracks use same buffer
int32_t* out = t0.mainBuffer;
size_t numFrames = state->frameCount;
- int16_t const *buff = NULL;
+ const int16_t *buff = NULL;
while (numFrames) {
@@ -1190,7 +1094,7 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
}
in1 = buff;
b1.frameCount = numFrames;
- } else {
+ } else {
in1 = b1.i16;
}
frameCount1 = b1.frameCount;
@@ -1225,11 +1129,9 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
}
}
- if (buff != NULL) {
- delete [] buff;
- }
+ delete [] buff;
}
+#endif
// ----------------------------------------------------------------------------
}; // namespace android
-
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 0137185..84f6330 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -28,11 +28,6 @@ namespace android {
// ----------------------------------------------------------------------------
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
-
-// ----------------------------------------------------------------------------
-
class AudioMixer
{
public:
@@ -47,11 +42,10 @@ public:
enum { // names
- // track units (32 units)
+ // track names (MAX_NUM_TRACKS units)
TRACK0 = 0x1000,
- // enable/disable
- MIXING = 0x2000,
+ // 0x2000 is unused
// setParameter targets
TRACK = 0x3000,
@@ -65,32 +59,30 @@ public:
FORMAT = 0x4001,
MAIN_BUFFER = 0x4002,
AUX_BUFFER = 0x4003,
- // for TARGET RESAMPLE
+ // for target RESAMPLE
SAMPLE_RATE = 0x4100,
RESET = 0x4101,
- // for TARGET VOLUME (8 channels max)
+ // for target RAMP_VOLUME and VOLUME (8 channels max)
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
AUXLEVEL = 0x4210,
};
+ // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
int getTrackName();
void deleteTrackName(int name);
- status_t enable(int name);
- status_t disable(int name);
+ void enable(int name);
+ void disable(int name);
- status_t setActiveTrack(int track);
- status_t setParameter(int target, int name, void *value);
+ void setParameter(int name, int target, int param, void *value);
- status_t setBufferProvider(AudioBufferProvider* bufferProvider);
+ void setBufferProvider(int name, AudioBufferProvider* bufferProvider);
void process();
uint32_t trackNames() const { return mTrackNames; }
- static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c);
-
size_t getUnreleasedFrames(int name);
private:
@@ -119,11 +111,6 @@ private:
NEEDS_AUX_ENABLED = 0x00010000,
};
- static inline int32_t applyVolume(int32_t in, int32_t v) {
- return in * v;
- }
-
-
struct state_t;
struct track_t;
@@ -135,13 +122,13 @@ private:
uint32_t needs;
union {
- int16_t volume[2]; // [0]3.12 fixed point
+ int16_t volume[MAX_NUM_CHANNELS]; // [0]3.12 fixed point
int32_t volumeRL;
};
- int32_t prevVolume[2];
+ int32_t prevVolume[MAX_NUM_CHANNELS];
- int32_t volumeInc[2];
+ int32_t volumeInc[MAX_NUM_CHANNELS];
int32_t auxLevel;
int32_t auxInc;
int32_t prevAuxLevel;
@@ -158,7 +145,7 @@ private:
mutable AudioBufferProvider::Buffer buffer;
hook_t hook;
- void const* in; // current location in buffer
+ const void* in; // current location in buffer
AudioResampler* resampler;
uint32_t sampleRate;
@@ -181,10 +168,10 @@ private:
int32_t *outputTemp;
int32_t *resampleTemp;
int32_t reserved[2];
- track_t tracks[32]; __attribute__((aligned(32)));
+ track_t tracks[MAX_NUM_TRACKS]; __attribute__((aligned(32)));
};
- int mActiveTrack;
+ // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
uint32_t mTrackNames;
const uint32_t mSampleRate;
@@ -204,7 +191,9 @@ private:
static void process__genericNoResampling(state_t* state);
static void process__genericResampling(state_t* state);
static void process__OneTrack16BitsStereoNoResampling(state_t* state);
+#if 0
static void process__TwoTracks16BitsStereoNoResampling(state_t* state);
+#endif
};
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 6be669b..1dddbb3 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -31,7 +31,6 @@
#include <utils/threads.h>
#include "AudioPolicyService.h"
#include <cutils/properties.h>
-#include <dlfcn.h>
#include <hardware_legacy/power.h>
#include <media/AudioEffect.h>
#include <media/EffectsFactoryApi.h>
@@ -44,11 +43,11 @@
namespace android {
-static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n";
-static const char *kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n";
+static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n";
+static const char kCmdDeadlockedString[] = "AudioPolicyService command thread may be deadlocked\n";
static const int kDumpLockRetries = 50;
-static const int kDumpLockSleep = 20000;
+static const int kDumpLockSleepUs = 20000;
static bool checkPermission() {
if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
@@ -145,9 +144,9 @@ AudioPolicyService::~AudioPolicyService()
}
mInputs.clear();
- if (mpAudioPolicy && mpAudioPolicyDev)
+ if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL)
mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy);
- if (mpAudioPolicyDev)
+ if (mpAudioPolicyDev != NULL)
audio_policy_dev_close(mpAudioPolicyDev);
}
@@ -186,7 +185,7 @@ audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState(
device_address);
}
-status_t AudioPolicyService::setPhoneState(int state)
+status_t AudioPolicyService::setPhoneState(audio_mode_t state)
{
if (mpAudioPolicy == NULL) {
return NO_INIT;
@@ -194,7 +193,7 @@ status_t AudioPolicyService::setPhoneState(int state)
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (state < 0 || state >= AUDIO_MODE_CNT) {
+ if (uint32_t(state) >= AUDIO_MODE_CNT) {
return BAD_VALUE;
}
@@ -208,19 +207,6 @@ status_t AudioPolicyService::setPhoneState(int state)
return NO_ERROR;
}
-status_t AudioPolicyService::setRingerMode(uint32_t mode, uint32_t mask)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- if (!checkPermission()) {
- return PERMISSION_DENIED;
- }
-
- mpAudioPolicy->set_ringer_mode(mpAudioPolicy, mode, mask);
- return NO_ERROR;
-}
-
status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,
audio_policy_forced_cfg_t config)
{
@@ -255,7 +241,7 @@ audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use
audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
uint32_t samplingRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channels,
audio_policy_output_flags_t flags)
{
@@ -301,9 +287,9 @@ void AudioPolicyService::releaseOutput(audio_io_handle_t output)
mpAudioPolicy->release_output(mpAudioPolicy, output);
}
-audio_io_handle_t AudioPolicyService::getInput(int inputSource,
+audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource,
uint32_t samplingRate,
- uint32_t format,
+ audio_format_t format,
uint32_t channels,
audio_in_acoustics_t acoustics,
int audioSession)
@@ -311,6 +297,10 @@ audio_io_handle_t AudioPolicyService::getInput(int inputSource,
if (mpAudioPolicy == NULL) {
return 0;
}
+ // already checked by client, but double-check in case the client wrapper is bypassed
+ if (uint32_t(inputSource) >= AUDIO_SOURCE_CNT) {
+ return 0;
+ }
Mutex::Autolock _l(mLock);
audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate,
format, channels, acoustics);
@@ -319,7 +309,7 @@ audio_io_handle_t AudioPolicyService::getInput(int inputSource,
return input;
}
// create audio pre processors according to input source
- ssize_t index = mInputSources.indexOfKey((audio_source_t)inputSource);
+ ssize_t index = mInputSources.indexOfKey(inputSource);
if (index < 0) {
return input;
}
@@ -401,14 +391,16 @@ status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax);
return NO_ERROR;
}
-status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, int index)
+status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
+ int index,
+ audio_devices_t device)
{
if (mpAudioPolicy == NULL) {
return NO_INIT;
@@ -416,22 +408,38 @@ status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, in
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
- return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index);
+ if (mpAudioPolicy->set_stream_volume_index_for_device) {
+ return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy,
+ stream,
+ index,
+ device);
+ } else {
+ return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index);
+ }
}
-status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, int *index)
+status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream,
+ int *index,
+ audio_devices_t device)
{
if (mpAudioPolicy == NULL) {
return NO_INIT;
}
- if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
+ if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
- return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index);
+ if (mpAudioPolicy->get_stream_volume_index_for_device) {
+ return mpAudioPolicy->get_stream_volume_index_for_device(mpAudioPolicy,
+ stream,
+ index,
+ device);
+ } else {
+ return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index);
+ }
}
uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream)
@@ -487,7 +495,7 @@ status_t AudioPolicyService::setEffectEnabled(int id, bool enabled)
return mpAudioPolicy->set_effect_enabled(mpAudioPolicy, id, enabled);
}
-bool AudioPolicyService::isStreamActive(int stream, uint32_t inPastMs) const
+bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
{
if (mpAudioPolicy == NULL) {
return 0;
@@ -546,7 +554,7 @@ static bool tryLock(Mutex& mutex)
locked = true;
break;
}
- usleep(kDumpLockSleep);
+ usleep(kDumpLockSleepUs);
}
return locked;
}
@@ -570,7 +578,7 @@ status_t AudioPolicyService::dumpInternals(int fd)
status_t AudioPolicyService::dump(int fd, const Vector<String16>& args)
{
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ if (!checkCallingPermission(String16("android.permission.DUMP"))) {
dumpPermissionDenial(fd);
} else {
bool locked = tryLock(mLock);
@@ -641,7 +649,7 @@ AudioPolicyService::AudioCommandThread::~AudioCommandThread()
release_wake_lock(mName.string());
}
mAudioCommands.clear();
- if (mpToneGenerator != NULL) delete mpToneGenerator;
+ delete mpToneGenerator;
}
void AudioPolicyService::AudioCommandThread::onFirstRef()
@@ -674,8 +682,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
ToneData *data = (ToneData *)command->mParam;
ALOGV("AudioCommandThread() processing start tone %d on stream %d",
data->mType, data->mStream);
- if (mpToneGenerator != NULL)
- delete mpToneGenerator;
+ delete mpToneGenerator;
mpToneGenerator = new ToneGenerator(data->mStream, 1.0);
mpToneGenerator->startTone(data->mType);
delete data;
@@ -782,7 +789,8 @@ status_t AudioPolicyService::AudioCommandThread::dump(int fd)
return NO_ERROR;
}
-void AudioPolicyService::AudioCommandThread::startToneCommand(int type, int stream)
+void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type,
+ audio_stream_type_t stream)
{
AudioCommand *command = new AudioCommand();
command->mCommand = START_TONE;
@@ -809,7 +817,7 @@ void AudioPolicyService::AudioCommandThread::stopToneCommand()
mWaitWorkCV.signal();
}
-status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream,
+status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream,
float volume,
int output,
int delayMs)
@@ -1020,7 +1028,7 @@ int AudioPolicyService::setStreamVolume(audio_stream_type_t stream,
audio_io_handle_t output,
int delayMs)
{
- return (int)mAudioCommandThread->volumeCommand((int)stream, volume,
+ return (int)mAudioCommandThread->volumeCommand(stream, volume,
(int)output, delayMs);
}
@@ -1052,7 +1060,7 @@ int AudioPolicyService::setVoiceVolume(float volume, int delayMs)
// Audio pre-processing configuration
// ----------------------------------------------------------------------------
-const char *AudioPolicyService::kInputSourceNames[AUDIO_SOURCE_CNT -1] = {
+/*static*/ const char * const AudioPolicyService::kInputSourceNames[AUDIO_SOURCE_CNT -1] = {
MIC_SRC_TAG,
VOICE_UL_SRC_TAG,
VOICE_DL_SRC_TAG,
@@ -1152,7 +1160,7 @@ effect_param_t *AudioPolicyService::loadEffectParameter(cnode *root)
if (param == NULL && value == NULL) {
// try to parse simple parameter form {int int}
param = root->first_child;
- if (param) {
+ if (param != NULL) {
// Note: that a pair of random strings is read as 0 0
int *ptr = (int *)fx_param->data;
int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
@@ -1348,7 +1356,7 @@ extern "C" {
static audio_io_handle_t aps_open_output(void *service,
uint32_t *pDevices,
uint32_t *pSamplingRate,
- uint32_t *pFormat,
+ audio_format_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
audio_policy_output_flags_t flags)
@@ -1409,9 +1417,9 @@ static int aps_restore_output(void *service, audio_io_handle_t output)
static audio_io_handle_t aps_open_input(void *service,
uint32_t *pDevices,
uint32_t *pSamplingRate,
- uint32_t *pFormat,
+ audio_format_t *pFormat,
uint32_t *pChannels,
- uint32_t acoustics)
+ audio_in_acoustics_t acoustics)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == NULL) {
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index d898a53..62219e5 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -19,6 +19,7 @@
#include <cutils/misc.h>
#include <cutils/config_utils.h>
+#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/SortedVector.h>
#include <binder/BinderService.h>
@@ -31,8 +32,6 @@
namespace android {
-class String8;
-
// ----------------------------------------------------------------------------
class AudioPolicyService :
@@ -59,13 +58,12 @@ public:
virtual audio_policy_dev_state_t getDeviceConnectionState(
audio_devices_t device,
const char *device_address);
- virtual status_t setPhoneState(int state);
- virtual status_t setRingerMode(uint32_t mode, uint32_t mask);
+ virtual status_t setPhoneState(audio_mode_t state);
virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
virtual audio_io_handle_t getOutput(audio_stream_type_t stream,
uint32_t samplingRate = 0,
- uint32_t format = AUDIO_FORMAT_DEFAULT,
+ audio_format_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
audio_policy_output_flags_t flags =
AUDIO_POLICY_OUTPUT_FLAG_INDIRECT);
@@ -76,12 +74,12 @@ public:
audio_stream_type_t stream,
int session = 0);
virtual void releaseOutput(audio_io_handle_t output);
- virtual audio_io_handle_t getInput(int inputSource,
+ virtual audio_io_handle_t getInput(audio_source_t inputSource,
uint32_t samplingRate = 0,
- uint32_t format = AUDIO_FORMAT_DEFAULT,
+ audio_format_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
audio_in_acoustics_t acoustics =
- (audio_in_acoustics_t)0,
+ (audio_in_acoustics_t)0 /*AUDIO_IN_ACOUSTICS_NONE*/,
int audioSession = 0);
virtual status_t startInput(audio_io_handle_t input);
virtual status_t stopInput(audio_io_handle_t input);
@@ -89,8 +87,12 @@ public:
virtual status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax);
- virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, int index);
- virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index);
+ virtual status_t setStreamVolumeIndex(audio_stream_type_t stream,
+ int index,
+ audio_devices_t device);
+ virtual status_t getStreamVolumeIndex(audio_stream_type_t stream,
+ int *index,
+ audio_devices_t device);
virtual uint32_t getStrategyForStream(audio_stream_type_t stream);
virtual uint32_t getDevicesForStream(audio_stream_type_t stream);
@@ -103,7 +105,7 @@ public:
int id);
virtual status_t unregisterEffect(int id);
virtual status_t setEffectEnabled(int id, bool enabled);
- virtual bool isStreamActive(int stream, uint32_t inPastMs = 0) const;
+ virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
virtual status_t queryDefaultPreProcessing(int audioSession,
effect_descriptor_t *descriptors,
@@ -169,9 +171,10 @@ private:
virtual bool threadLoop();
void exit();
- void startToneCommand(int type = 0, int stream = 0);
+ void startToneCommand(ToneGenerator::tone_type type,
+ audio_stream_type_t stream);
void stopToneCommand();
- status_t volumeCommand(int stream, float volume, int output, int delayMs = 0);
+ status_t volumeCommand(audio_stream_type_t stream, float volume, int output, int delayMs = 0);
status_t parametersCommand(int ioHandle, const char *keyValuePairs, int delayMs = 0);
status_t voiceVolumeCommand(float volume, int delayMs = 0);
void insertCommand_l(AudioCommand *command, int delayMs = 0);
@@ -196,13 +199,13 @@ private:
class ToneData {
public:
- int mType; // tone type (START_TONE only)
- int mStream; // stream type (START_TONE only)
+ ToneGenerator::tone_type mType; // tone type (START_TONE only)
+ audio_stream_type_t mStream; // stream type (START_TONE only)
};
class VolumeData {
public:
- int mStream;
+ audio_stream_type_t mStream;
float mVolume;
int mIO;
};
@@ -251,7 +254,7 @@ private:
Vector< sp<AudioEffect> >mEffects;
};
- static const char *kInputSourceNames[AUDIO_SOURCE_CNT -1];
+ static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
void setPreProcessorEnabled(InputDesc *inputDesc, bool enabled);
status_t loadPreProcessorConfig(const char *path);
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 4586b54..feacd96 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -390,6 +390,7 @@ resampleMono16_exit:
* phaseFraction : phase fraction for next interpolation
*
*******************************************************************/
+__attribute__((noinline))
void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
uint32_t &phaseFraction, uint32_t phaseIncrement)
@@ -500,6 +501,7 @@ void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t
* phaseFraction : phase fraction for next interpolation
*
*******************************************************************/
+__attribute__((noinline))
void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
uint32_t &phaseFraction, uint32_t phaseIncrement)
@@ -600,6 +602,5 @@ void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32
// ----------------------------------------------------------------------------
-}
-; // namespace android
+} // namespace android
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index 9e5e254..d012433 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -284,7 +284,7 @@ template<int CHANNELS>
**/
void AudioResamplerSinc::read(
int16_t*& impulse, uint32_t& phaseFraction,
- int16_t const* in, size_t inputIndex)
+ const int16_t* in, size_t inputIndex)
{
const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
impulse += CHANNELS;
@@ -302,7 +302,7 @@ void AudioResamplerSinc::read(
template<int CHANNELS>
void AudioResamplerSinc::filterCoefficient(
- int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples)
+ int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples)
{
// compute the index of the coefficient on the positive side and
// negative side
@@ -317,9 +317,9 @@ void AudioResamplerSinc::filterCoefficient(
l = 0;
r = 0;
- int32_t const* coefs = mFirCoefs;
- int16_t const *sP = samples;
- int16_t const *sN = samples+CHANNELS;
+ const int32_t* coefs = mFirCoefs;
+ const int16_t *sP = samples;
+ const int16_t *sN = samples+CHANNELS;
for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) {
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
@@ -339,13 +339,13 @@ void AudioResamplerSinc::filterCoefficient(
template<int CHANNELS>
void AudioResamplerSinc::interpolate(
int32_t& l, int32_t& r,
- int32_t const* coefs, int16_t lerp, int16_t const* samples)
+ const int32_t* coefs, int16_t lerp, const int16_t* samples)
{
int32_t c0 = coefs[0];
int32_t c1 = coefs[1];
int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0);
if (CHANNELS == 2) {
- uint32_t rl = *reinterpret_cast<uint32_t const*>(samples);
+ uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
l = mulAddRL(1, rl, sinc, l);
r = mulAddRL(0, rl, sinc, r);
} else {
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index e6cb90b..0e1bc44 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -44,22 +44,22 @@ private:
template<int CHANNELS>
inline void filterCoefficient(
- int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples);
+ int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples);
template<int CHANNELS>
inline void interpolate(
int32_t& l, int32_t& r,
- int32_t const* coefs, int16_t lerp, int16_t const* samples);
+ const int32_t* coefs, int16_t lerp, const int16_t* samples);
template<int CHANNELS>
inline void read(int16_t*& impulse, uint32_t& phaseFraction,
- int16_t const* in, size_t inputIndex);
+ const int16_t* in, size_t inputIndex);
int16_t *mState;
int16_t *mImpulse;
int16_t *mRingFull;
- int32_t const * mFirCoefs;
+ const int32_t * mFirCoefs;
static const int32_t mFirCoefsDown[];
static const int32_t mFirCoefsUp[];
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
index 34087b5..2ac69f7 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -635,6 +635,12 @@ private:
return native_window_set_crop(a, &crop);
}
+ static int __set_timestamp(struct preview_stream_ops *w,
+ int64_t timestamp) {
+ ANativeWindow *a = anw(w);
+ return native_window_set_buffers_timestamp(a, timestamp);
+ }
+
static int __set_usage(struct preview_stream_ops* w, int usage)
{
ANativeWindow *a = anw(w);
@@ -664,6 +670,7 @@ private:
mHalPreviewWindow.nw.set_buffer_count = __set_buffer_count;
mHalPreviewWindow.nw.set_buffers_geometry = __set_buffers_geometry;
mHalPreviewWindow.nw.set_crop = __set_crop;
+ mHalPreviewWindow.nw.set_timestamp = __set_timestamp;
mHalPreviewWindow.nw.set_usage = __set_usage;
mHalPreviewWindow.nw.set_swap_interval = __set_swap_interval;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 918f31e..06fc708 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -351,7 +351,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
// Enable zoom, error, focus, and metadata messages by default
enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
- CAMERA_MSG_PREVIEW_METADATA);
+ CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE);
// Callback is disabled by default
mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 68bbb570..296c95e 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -156,8 +156,6 @@ EventHub::EventHub(void) :
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
- mNumCpus = sysconf(_SC_NPROCESSORS_ONLN);
-
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
@@ -648,8 +646,9 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// Device was removed before INotify noticed.
- ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n",
- device->fd, readSize, bufferSize, capacity, errno);
+ ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d "
+ "capacity: %d errno: %d)\n",
+ device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
} else if (readSize < 0) {
@@ -774,19 +773,6 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
-
- // On an SMP system, it is possible for the framework to read input events
- // faster than the kernel input device driver can produce a complete packet.
- // Because poll() wakes up as soon as the first input event becomes available,
- // the framework will often end up reading one event at a time until the
- // packet is complete. Instead of one call to read() returning 71 events,
- // it could take 71 calls to read() each returning 1 event.
- //
- // Sleep for a short period of time after waking up from the poll() to give
- // the kernel time to finish writing the entire packet of input events.
- if (mNumCpus > 1) {
- usleep(250);
- }
}
}
@@ -845,7 +831,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
ALOGV("Opening device: %s", devicePath);
- int fd = open(devicePath, O_RDWR);
+ int fd = open(devicePath, O_RDWR | O_CLOEXEC);
if(fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
@@ -1077,14 +1063,20 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
return -1;
}
+ // Enable wake-lock behavior on kernels that support it.
+ // TODO: Only need this for devices that can really wake the system.
+ bool usingSuspendBlock = ioctl(fd, EVIOCSSUSPENDBLOCK, 1) == 0;
+
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
- "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s",
+ "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
+ "usingSuspendBlock=%s",
deviceId, fd, devicePath, device->identifier.name.string(),
device->classes,
device->configurationFile.string(),
device->keyMap.keyLayoutFile.string(),
device->keyMap.keyCharacterMapFile.string(),
- toString(mBuiltInKeyboardId == deviceId));
+ toString(mBuiltInKeyboardId == deviceId),
+ toString(usingSuspendBlock));
mDevices.add(deviceId, device);
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 9d8252e..8a2afd3 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -367,9 +367,6 @@ private:
size_t mPendingEventCount;
size_t mPendingEventIndex;
bool mPendingINotify;
-
- // Set to the number of CPUs.
- int32_t mNumCpus;
};
}; // namespace android
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 0bef0db..ad64ccd 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -177,14 +177,6 @@ static bool validateMotionEvent(int32_t action, size_t pointerCount,
return true;
}
-static void scalePointerCoords(const PointerCoords* inCoords, size_t count, float scaleFactor,
- PointerCoords* outCoords) {
- for (size_t i = 0; i < count; i++) {
- outCoords[i] = inCoords[i];
- outCoords[i].scale(scaleFactor);
- }
-}
-
static void dumpRegion(String8& dump, const SkRegion& region) {
if (region.isEmpty()) {
dump.append("<empty>");
@@ -246,6 +238,8 @@ void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
+ mDispatcherIsAliveCondition.broadcast();
+
dispatchOnceInnerLocked(&nextWakeupTime);
if (runCommandsLockedInterruptible()) {
@@ -4094,6 +4088,8 @@ void InputDispatcher::dump(String8& dump) {
void InputDispatcher::monitor() {
// Acquire and release the lock to ensure that the dispatcher has not deadlocked.
mLock.lock();
+ mLooper->wake();
+ mDispatcherIsAliveCondition.wait(mLock);
mLock.unlock();
}
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 1478d67..a1d42e1 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -862,6 +862,8 @@ private:
Mutex mLock;
+ Condition mDispatcherIsAliveCondition;
+
sp<Looper> mLooper;
EventEntry* mPendingEvent;
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 8324d95..4be06e4 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -278,17 +278,20 @@ void InputReader::loopOnce() {
{ // acquire lock
AutoMutex _l(mLock);
+ mReaderIsAliveCondition.broadcast();
if (count) {
processEventsLocked(mEventBuffer, count);
}
if (!count || timeoutMillis == 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
- ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
+ ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
- mNextTimeout = LLONG_MAX;
- timeoutExpiredLocked(now);
+ mNextTimeout = LLONG_MAX;
+ timeoutExpiredLocked(now);
+ }
}
} // release lock
@@ -644,9 +647,13 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32
for (size_t i = 0; i < numDevices; i++) {
InputDevice* device = mDevices.valueAt(i);
if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
- result = (device->*getStateFunc)(sourceMask, code);
- if (result >= AKEY_STATE_DOWN) {
- return result;
+ // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
+ // value. Otherwise, return AKEY_STATE_UP as long as one device reports it.
+ int32_t currentResult = (device->*getStateFunc)(sourceMask, code);
+ if (currentResult >= AKEY_STATE_DOWN) {
+ return currentResult;
+ } else if (currentResult == AKEY_STATE_UP) {
+ result = currentResult;
}
}
}
@@ -768,6 +775,8 @@ void InputReader::dump(String8& dump) {
void InputReader::monitor() {
// Acquire and release the lock to ensure that the reader has not deadlocked.
mLock.lock();
+ mEventHub->wake();
+ mReaderIsAliveCondition.wait(mLock);
mLock.unlock();
// Check the EventHub
@@ -1000,9 +1009,13 @@ int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc ge
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
- result = (mapper->*getStateFunc)(sourceMask, code);
- if (result >= AKEY_STATE_DOWN) {
- return result;
+ // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
+ // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
+ int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
+ if (currentResult >= AKEY_STATE_DOWN) {
+ return currentResult;
+ } else if (currentResult == AKEY_STATE_UP) {
+ result = currentResult;
}
}
}
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index a122c97..ad89a22 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -363,6 +363,8 @@ protected:
private:
Mutex mLock;
+ Condition mReaderIsAliveCondition;
+
sp<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
sp<QueuedInputListener> mQueuedListener;
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 4f81178..081f1f4 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -24,67 +24,35 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.Intent.FilterComparison;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
-import android.util.TypedValue;
-import android.util.Xml;
+import android.util.SparseArray;
import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
-import com.android.internal.widget.IRemoteViewsFactory;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
-import java.util.Set;
+
+/**
+ * Redirects calls to this service to the instance of the service for the appropriate user.
+ */
class AppWidgetService extends IAppWidgetService.Stub
{
private static final String TAG = "AppWidgetService";
- private static final String SETTINGS_FILENAME = "appwidgets.xml";
- private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
-
/*
* When identifying a Host or Provider based on the calling process, use the uid field.
* When identifying a Host or Provider based on a package manager broadcast, use the
@@ -125,11 +93,9 @@ class AppWidgetService extends IAppWidgetService.Stub
* globally and may lead us to leak AppWidgetService instances (if there were more than one).
*/
static class ServiceConnectionProxy implements ServiceConnection {
- private final Pair<Integer, Intent.FilterComparison> mKey;
private final IBinder mConnectionCb;
ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
- mKey = key;
mConnectionCb = connectionCb;
}
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -155,13 +121,6 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
- // Manages active connections to RemoteViewsServices
- private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
- mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
- // Manages persistent references to RemoteViewsServices from different App Widgets
- private final HashMap<FilterComparison, HashSet<Integer>>
- mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
-
Context mContext;
Locale mLocale;
PackageManager mPackageManager;
@@ -171,35 +130,32 @@ class AppWidgetService extends IAppWidgetService.Stub
final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
ArrayList<Host> mHosts = new ArrayList<Host>();
boolean mSafeMode;
- boolean mStateLoaded;
- // These are for debugging only -- widgets are going missing in some rare instances
- ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
- ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+ private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
AppWidgetService(Context context) {
mContext = context;
- mPackageManager = context.getPackageManager();
- mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
+ AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
+ mAppWidgetServices.append(0, primary);
}
public void systemReady(boolean safeMode) {
mSafeMode = safeMode;
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- }
+ mAppWidgetServices.get(0).systemReady(safeMode);
// Register for the boot completed broadcast, so we can send the
- // ENABLE broacasts. If we try to send them now, they time out,
+ // ENABLE broacasts. If we try to send them now, they time out,
// because the system isn't ready to handle them yet.
mContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
// Register for configuration changes so we can update the names
// of the widgets when the locale changes.
- mContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+ mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
+ Intent.ACTION_CONFIGURATION_CHANGED), null, null);
// Register for broadcasts about package install, etc., so we can
// update the provider list.
@@ -216,216 +172,24 @@ class AppWidgetService extends IAppWidgetService.Stub
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
}
- private void ensureStateLoadedLocked() {
- if (!mStateLoaded) {
- loadAppWidgetList();
- loadStateLocked();
- mStateLoaded = true;
- }
- }
-
- private void dumpProvider(Provider p, int index, PrintWriter pw) {
- AppWidgetProviderInfo info = p.info;
- pw.print(" ["); pw.print(index); pw.print("] provider ");
- pw.print(info.provider.flattenToShortString());
- pw.println(':');
- pw.print(" min=("); pw.print(info.minWidth);
- pw.print("x"); pw.print(info.minHeight);
- pw.print(") minResize=("); pw.print(info.minResizeWidth);
- pw.print("x"); pw.print(info.minResizeHeight);
- pw.print(") updatePeriodMillis=");
- pw.print(info.updatePeriodMillis);
- pw.print(" resizeMode=");
- pw.print(info.resizeMode);
- pw.print(" autoAdvanceViewId=");
- pw.print(info.autoAdvanceViewId);
- pw.print(" initialLayout=#");
- pw.print(Integer.toHexString(info.initialLayout));
- pw.print(" zombie="); pw.println(p.zombie);
- }
-
- private void dumpHost(Host host, int index, PrintWriter pw) {
- pw.print(" ["); pw.print(index); pw.print("] hostId=");
- pw.print(host.hostId); pw.print(' ');
- pw.print(host.packageName); pw.print('/');
- pw.print(host.uid); pw.println(':');
- pw.print(" callbacks="); pw.println(host.callbacks);
- pw.print(" instances.size="); pw.print(host.instances.size());
- pw.print(" zombie="); pw.println(host.zombie);
- }
-
- private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
- pw.print(" ["); pw.print(index); pw.print("] id=");
- pw.println(id.appWidgetId);
- pw.print(" hostId=");
- pw.print(id.host.hostId); pw.print(' ');
- pw.print(id.host.packageName); pw.print('/');
- pw.println(id.host.uid);
- if (id.provider != null) {
- pw.print(" provider=");
- pw.println(id.provider.info.provider.flattenToShortString());
- }
- if (id.host != null) {
- pw.print(" host.callbacks="); pw.println(id.host.callbacks);
- }
- if (id.views != null) {
- pw.print(" views="); pw.println(id.views);
- }
- }
-
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mAppWidgetIds) {
- int N = mInstalledProviders.size();
- pw.println("Providers:");
- for (int i=0; i<N; i++) {
- dumpProvider(mInstalledProviders.get(i), i, pw);
- }
-
- N = mAppWidgetIds.size();
- pw.println(" ");
- pw.println("AppWidgetIds:");
- for (int i=0; i<N; i++) {
- dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
- }
-
- N = mHosts.size();
- pw.println(" ");
- pw.println("Hosts:");
- for (int i=0; i<N; i++) {
- dumpHost(mHosts.get(i), i, pw);
- }
-
- N = mDeletedProviders.size();
- pw.println(" ");
- pw.println("Deleted Providers:");
- for (int i=0; i<N; i++) {
- dumpProvider(mDeletedProviders.get(i), i, pw);
- }
-
- N = mDeletedHosts.size();
- pw.println(" ");
- pw.println("Deleted Hosts:");
- for (int i=0; i<N; i++) {
- dumpHost(mDeletedHosts.get(i), i, pw);
- }
- }
- }
-
- public int allocateAppWidgetId(String packageName, int hostId) {
- int callingUid = enforceCallingUid(packageName);
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int appWidgetId = mNextAppWidgetId++;
-
- Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
-
- AppWidgetId id = new AppWidgetId();
- id.appWidgetId = appWidgetId;
- id.host = host;
-
- host.instances.add(id);
- mAppWidgetIds.add(id);
-
- saveStateLocked();
-
- return appWidgetId;
- }
+ public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
+ return getImplForUser().allocateAppWidgetId(packageName, hostId);
}
-
- public void deleteAppWidgetId(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null) {
- deleteAppWidgetLocked(id);
- saveStateLocked();
- }
- }
- }
-
- public void deleteHost(int hostId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int callingUid = getCallingUid();
- Host host = lookupHostLocked(callingUid, hostId);
- if (host != null) {
- deleteHostLocked(host);
- saveStateLocked();
- }
- }
- }
-
- public void deleteAllHosts() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int callingUid = getCallingUid();
- final int N = mHosts.size();
- boolean changed = false;
- for (int i=N-1; i>=0; i--) {
- Host host = mHosts.get(i);
- if (host.uid == callingUid) {
- deleteHostLocked(host);
- changed = true;
- }
- }
- if (changed) {
- saveStateLocked();
- }
- }
+
+ @Override
+ public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
+ getImplForUser().deleteAppWidgetId(appWidgetId);
}
- void deleteHostLocked(Host host) {
- final int N = host.instances.size();
- for (int i=N-1; i>=0; i--) {
- AppWidgetId id = host.instances.get(i);
- deleteAppWidgetLocked(id);
- }
- host.instances.clear();
- mHosts.remove(host);
- mDeletedHosts.add(host);
- // it's gone or going away, abruptly drop the callback connection
- host.callbacks = null;
+ @Override
+ public void deleteHost(int hostId) throws RemoteException {
+ getImplForUser().deleteHost(hostId);
}
- void deleteAppWidgetLocked(AppWidgetId id) {
- // We first unbind all services that are bound to this id
- unbindAppWidgetRemoteViewsServicesLocked(id);
-
- Host host = id.host;
- host.instances.remove(id);
- pruneHostLocked(host);
-
- mAppWidgetIds.remove(id);
-
- Provider p = id.provider;
- if (p != null) {
- p.instances.remove(id);
- if (!p.zombie) {
- // send the broacast saying that this appWidgetId has been deleted
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
- intent.setComponent(p.info.provider);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
- mContext.sendBroadcast(intent);
- if (p.instances.size() == 0) {
- // cancel the future updates
- cancelBroadcasts(p);
-
- // send the broacast saying that the provider is not in use any more
- intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
- }
- }
- }
+ @Override
+ public void deleteAllHosts() throws RemoteException {
+ getImplForUser().deleteAllHosts();
}
void cancelBroadcasts(Provider p) {
@@ -441,617 +205,58 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
- public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
- mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
- "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
- if (id.provider != null) {
- throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
- + id.provider.info.provider);
- }
- Provider p = lookupProviderLocked(provider);
- if (p == null) {
- throw new IllegalArgumentException("not a appwidget provider: " + provider);
- }
- if (p.zombie) {
- throw new IllegalArgumentException("can't bind to a 3rd party provider in"
- + " safe mode: " + provider);
- }
-
- id.provider = p;
- p.instances.add(id);
- int instancesSize = p.instances.size();
- if (instancesSize == 1) {
- // tell the provider that it's ready
- sendEnableIntentLocked(p);
- }
-
- // send an update now -- We need this update now, and just for this appWidgetId.
- // It's less critical when the next one happens, so when we schdule the next one,
- // we add updatePeriodMillis to its start time. That time will have some slop,
- // but that's okay.
- sendUpdateIntentLocked(p, new int[] { appWidgetId });
-
- // schedule the future updates
- registerForBroadcastsLocked(p, getAppWidgetIds(p));
- saveStateLocked();
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- // Binds to a specific RemoteViewsService
- public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
- final ComponentName componentName = intent.getComponent();
- try {
- final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
- PackageManager.GET_PERMISSIONS);
- if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
- throw new SecurityException("Selected service does not require "
- + android.Manifest.permission.BIND_REMOTEVIEWS
- + ": " + componentName);
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("Unknown component " + componentName);
- }
-
- // If there is already a connection made for this service intent, then disconnect from
- // that first. (This does not allow multiple connections to the same service under
- // the same key)
- ServiceConnectionProxy conn = null;
- FilterComparison fc = new FilterComparison(intent);
- Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
- if (mBoundRemoteViewsServices.containsKey(key)) {
- conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- mBoundRemoteViewsServices.remove(key);
- }
-
- // Bind to the RemoteViewsService (which will trigger a callback to the
- // RemoteViewsAdapter.onServiceConnected())
- final long token = Binder.clearCallingIdentity();
- try {
- conn = new ServiceConnectionProxy(key, connection);
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- mBoundRemoteViewsServices.put(key, conn);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
- // when we can call back to the RemoteViewsService later to destroy associated
- // factories.
- incrementAppWidgetServiceRefCount(appWidgetId, fc);
- }
- }
-
- // Unbinds from a specific RemoteViewsService
- public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- // Unbind from the RemoteViewsService (which will trigger a callback to the bound
- // RemoteViewsAdapter)
- Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
- new FilterComparison(intent));
- if (mBoundRemoteViewsServices.containsKey(key)) {
- // We don't need to use the appWidgetId until after we are sure there is something
- // to unbind. Note that this may mask certain issues with apps calling unbind()
- // more than necessary.
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
-
- ServiceConnectionProxy conn =
- (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- mBoundRemoteViewsServices.remove(key);
- } else {
- Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
- }
- }
- }
-
- // Unbinds from a RemoteViewsService when we delete an app widget
- private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
- int appWidgetId = id.appWidgetId;
- // Unbind all connections to Services bound to this AppWidgetId
- Iterator<Pair<Integer, Intent.FilterComparison>> it =
- mBoundRemoteViewsServices.keySet().iterator();
- while (it.hasNext()) {
- final Pair<Integer, Intent.FilterComparison> key = it.next();
- if (key.first.intValue() == appWidgetId) {
- final ServiceConnectionProxy conn = (ServiceConnectionProxy)
- mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- it.remove();
- }
- }
-
- // Check if we need to destroy any services (if no other app widgets are
- // referencing the same service)
- decrementAppWidgetServiceRefCount(appWidgetId);
- }
-
- // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
- private void destroyRemoteViewsService(final Intent intent) {
- final ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- final IRemoteViewsFactory cb =
- IRemoteViewsFactory.Stub.asInterface(service);
- try {
- cb.onDestroy(intent);
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (RuntimeException e) {
- e.printStackTrace();
- }
- mContext.unbindService(this);
- }
- @Override
- public void onServiceDisconnected(android.content.ComponentName name) {
- // Do nothing
- }
- };
-
- // Bind to the service and remove the static intent->factory mapping in the
- // RemoteViewsService.
- final long token = Binder.clearCallingIdentity();
- try {
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- // Adds to the ref-count for a given RemoteViewsService intent
- private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
- HashSet<Integer> appWidgetIds = null;
- if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
- appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
- } else {
- appWidgetIds = new HashSet<Integer>();
- mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
- }
- appWidgetIds.add(appWidgetId);
- }
-
- // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
- // the ref-count reaches zero.
- private void decrementAppWidgetServiceRefCount(int appWidgetId) {
- Iterator<FilterComparison> it =
- mRemoteViewsServicesAppWidgets.keySet().iterator();
- while (it.hasNext()) {
- final FilterComparison key = it.next();
- final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
- if (ids.remove(appWidgetId)) {
- // If we have removed the last app widget referencing this service, then we
- // should destroy it and remove it from this set
- if (ids.isEmpty()) {
- destroyRemoteViewsService(key.getIntent());
- it.remove();
- }
- }
- }
- }
-
- public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null && id.provider != null && !id.provider.zombie) {
- return id.provider.info;
- }
- return null;
- }
- }
-
- public RemoteViews getAppWidgetViews(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null) {
- return id.views;
- }
- return null;
- }
- }
-
- public List<AppWidgetProviderInfo> getInstalledProviders() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- final int N = mInstalledProviders.size();
- ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (!p.zombie) {
- result.add(p.info);
- }
- }
- return result;
- }
- }
-
- public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- updateAppWidgetInstanceLocked(id, views);
- }
- }
- }
-
- public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- updateAppWidgetInstanceLocked(id, views, true);
- }
- }
- }
-
- public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
- }
- }
- }
-
- public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Provider p = lookupProviderLocked(provider);
- if (p == null) {
- Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
- 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);
- if (canAccessAppWidgetId(id, callingUid)) {
- updateAppWidgetInstanceLocked(id, views);
- }
- }
- }
- }
-
- void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
- updateAppWidgetInstanceLocked(id, views, false);
- }
-
- void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
- // allow for stale appWidgetIds and other badness
- // lookup also checks that the calling process can access the appWidgetId
- // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
- if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-
- // We do not want to save this RemoteViews
- if (!isPartialUpdate) id.views = views;
-
- // is anyone listening?
- if (id.host.callbacks != null) {
- try {
- // the lock is held, but this is a oneway call
- id.host.callbacks.updateAppWidget(id.appWidgetId, views);
- } catch (RemoteException e) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this instance.
- id.host.callbacks = null;
- }
- }
- }
- }
-
- void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
- // allow for stale appWidgetIds and other badness
- // lookup also checks that the calling process can access the appWidgetId
- // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
- if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
- // is anyone listening?
- if (id.host.callbacks != null) {
- try {
- // the lock is held, but this is a oneway call
- id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
- } catch (RemoteException e) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this instance.
- id.host.callbacks = null;
- }
- }
-
- // If the host is unavailable, then we call the associated
- // RemoteViewsFactory.onDataSetChanged() directly
- if (id.host.callbacks == null) {
- Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
- for (FilterComparison key : keys) {
- if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
- Intent intent = key.getIntent();
-
- final ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IRemoteViewsFactory cb =
- IRemoteViewsFactory.Stub.asInterface(service);
- try {
- cb.onDataSetChangedAsync();
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (RuntimeException e) {
- e.printStackTrace();
- }
- mContext.unbindService(this);
- }
- @Override
- public void onServiceDisconnected(android.content.ComponentName name) {
- // Do nothing
- }
- };
-
- // Bind to the service and call onDataSetChanged()
- final long token = Binder.clearCallingIdentity();
- try {
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
- }
- }
- }
-
- public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
- List<RemoteViews> updatedViews) {
- int callingUid = enforceCallingUid(packageName);
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
- host.callbacks = callbacks;
-
- updatedViews.clear();
-
- ArrayList<AppWidgetId> instances = host.instances;
- int N = instances.size();
- int[] updatedIds = new int[N];
- for (int i=0; i<N; i++) {
- AppWidgetId id = instances.get(i);
- updatedIds[i] = id.appWidgetId;
- updatedViews.add(id.views);
- }
- return updatedIds;
- }
- }
-
- public void stopListening(int hostId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Host host = lookupHostLocked(getCallingUid(), hostId);
- if (host != null) {
- host.callbacks = null;
- pruneHostLocked(host);
- }
- }
- }
-
- boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
- if (id.host.uid == callingUid) {
- // Apps hosting the AppWidget have access to it.
- return true;
- }
- if (id.provider != null && id.provider.uid == callingUid) {
- // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
- return true;
- }
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
- == PackageManager.PERMISSION_GRANTED) {
- // Apps that can bind have access to all appWidgetIds.
- return true;
- }
- // Nobody else can access it.
- return false;
- }
-
- AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
- int callingUid = getCallingUid();
- final int N = mAppWidgetIds.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = mAppWidgetIds.get(i);
- if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
- return id;
- }
- }
- return null;
- }
-
- Provider lookupProviderLocked(ComponentName provider) {
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.info.provider.equals(provider)) {
- return p;
- }
- }
- return null;
+ @Override
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
+ getImplForUser().bindAppWidgetId(appWidgetId, provider);
}
- Host lookupHostLocked(int uid, int hostId) {
- final int N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host h = mHosts.get(i);
- if (h.uid == uid && h.hostId == hostId) {
- return h;
- }
- }
- return null;
+ @Override
+ public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
+ throws RemoteException {
+ getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
}
- Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
- final int N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host h = mHosts.get(i);
- if (h.hostId == hostId && h.packageName.equals(packageName)) {
- return h;
- }
- }
- Host host = new Host();
- host.packageName = packageName;
- host.uid = uid;
- host.hostId = hostId;
- mHosts.add(host);
- return host;
+ @Override
+ public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
+ List<RemoteViews> updatedViews) throws RemoteException {
+ return getImplForUser().startListening(host, packageName, hostId, updatedViews);
}
- void pruneHostLocked(Host host) {
- if (host.instances.size() == 0 && host.callbacks == null) {
- mHosts.remove(host);
- }
+ // TODO: Call this from PackageManagerService when a user is removed
+ public void removeUser(int userId) {
}
- void loadAppWidgetList() {
- PackageManager pm = mPackageManager;
-
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- addProviderLocked(ri);
+ private AppWidgetServiceImpl getImplForUser() {
+ final int userId = Binder.getOrigCallingUser();
+ AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
+ if (service == null) {
+ Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
+ // TODO: Verify that it's a valid user
+ service = new AppWidgetServiceImpl(mContext, userId);
+ service.systemReady(mSafeMode);
+ // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
+ service.sendInitialBroadcasts();
+ mAppWidgetServices.append(userId, service);
}
- }
- boolean addProviderLocked(ResolveInfo ri) {
- if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- return false;
- }
- if (!ri.activityInfo.isEnabled()) {
- return false;
- }
- Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
- ri.activityInfo.name), ri);
- if (p != null) {
- mInstalledProviders.add(p);
- return true;
- } else {
- return false;
- }
+ return service;
}
- void removeProviderLocked(int index, Provider p) {
- int N = p.instances.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = p.instances.get(i);
- // Call back with empty RemoteViews
- updateAppWidgetInstanceLocked(id, null);
- // Stop telling the host about updates for this from now on
- cancelBroadcasts(p);
- // clear out references to this appWidgetId
- id.host.instances.remove(id);
- mAppWidgetIds.remove(id);
- id.provider = null;
- pruneHostLocked(id.host);
- id.host = null;
- }
- p.instances.clear();
- mInstalledProviders.remove(index);
- mDeletedProviders.add(p);
- // no need to send the DISABLE broadcast, since the receiver is gone anyway
- cancelBroadcasts(p);
+ @Override
+ public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
+ return getImplForUser().getAppWidgetIds(provider);
}
- void sendEnableIntentLocked(Provider p) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
+ @Override
+ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
+ return getImplForUser().getAppWidgetInfo(appWidgetId);
}
- void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
- if (appWidgetIds != null && appWidgetIds.length > 0) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
- }
+ @Override
+ public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
+ return getImplForUser().getAppWidgetViews(appWidgetId);
}
- void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
- if (p.info.updatePeriodMillis > 0) {
- // if this is the first instance, set the alarm. otherwise,
- // rely on the fact that we've already set it and that
- // PendingIntent.getBroadcast will update the extras.
- boolean alreadyRegistered = p.broadcast != null;
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
- intent.setComponent(p.info.provider);
- long token = Binder.clearCallingIdentity();
- try {
- p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- if (!alreadyRegistered) {
- long period = p.info.updatePeriodMillis;
- if (period < MIN_UPDATE_PERIOD) {
- period = MIN_UPDATE_PERIOD;
- }
- mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, p.broadcast);
- }
- }
- }
-
static int[] getAppWidgetIds(Provider p) {
int instancesSize = p.instances.size();
int appWidgetIds[] = new int[instancesSize];
@@ -1060,570 +265,70 @@ class AppWidgetService extends IAppWidgetService.Stub
}
return appWidgetIds;
}
-
- public int[] getAppWidgetIds(ComponentName provider) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Provider p = lookupProviderLocked(provider);
- if (p != null && getCallingUid() == p.uid) {
- return getAppWidgetIds(p);
- } else {
- return new int[0];
- }
- }
- }
-
- private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
- Provider p = null;
-
- ActivityInfo activityInfo = ri.activityInfo;
- XmlResourceParser parser = null;
- try {
- parser = activityInfo.loadXmlMetaData(mPackageManager,
- AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
- if (parser == null) {
- Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
- + "AppWidget provider '" + component + '\'');
- return null;
- }
-
- AttributeSet attrs = Xml.asAttributeSet(parser);
-
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.START_TAG) {
- // drain whitespace, comments, etc.
- }
-
- String nodeName = parser.getName();
- if (!"appwidget-provider".equals(nodeName)) {
- Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
- + " AppWidget provider '" + component + '\'');
- return null;
- }
-
- p = new Provider();
- AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
- info.provider = component;
- p.uid = activityInfo.applicationInfo.uid;
-
- Resources res = mPackageManager.getResourcesForApplication(
- activityInfo.applicationInfo);
-
- TypedArray sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AppWidgetProviderInfo);
- // These dimensions has to be resolved in the application's context.
- // We simply send back the raw complex data, which will be
- // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
- TypedValue value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
- info.minWidth = value != null ? value.data : 0;
- value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
- info.minHeight = value != null ? value.data : 0;
- value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
- info.minResizeWidth = value != null ? value.data : info.minWidth;
- value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
- info.minResizeHeight = value != null ? value.data : info.minHeight;
-
- info.updatePeriodMillis = sa.getInt(
- com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
- info.initialLayout = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
- String className = sa.getString(
- com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
- if (className != null) {
- info.configure = new ComponentName(component.getPackageName(), className);
- }
- info.label = activityInfo.loadLabel(mPackageManager).toString();
- info.icon = ri.getIconResource();
- info.previewImage = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
- info.autoAdvanceViewId = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
- info.resizeMode = sa.getInt(
- com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
- AppWidgetProviderInfo.RESIZE_NONE);
-
- sa.recycle();
- } catch (Exception e) {
- // Ok to catch Exception here, because anything going wrong because
- // of what a client process passes to us should not be fatal for the
- // system process.
- Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
- return null;
- } finally {
- if (parser != null) parser.close();
- }
- return p;
+ @Override
+ public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
+ return getImplForUser().getInstalledProviders();
}
- int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
- PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
- if (pkgInfo == null || pkgInfo.applicationInfo == null) {
- throw new PackageManager.NameNotFoundException();
- }
- return pkgInfo.applicationInfo.uid;
+ @Override
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
+ throws RemoteException {
+ getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
}
- int enforceCallingUid(String packageName) throws IllegalArgumentException {
- int callingUid = getCallingUid();
- int packageUid;
- try {
- packageUid = getUidForPackage(packageName);
- } catch (PackageManager.NameNotFoundException ex) {
- throw new IllegalArgumentException("packageName and uid don't match packageName="
- + packageName);
- }
- if (callingUid != packageUid) {
- throw new IllegalArgumentException("packageName and uid don't match packageName="
- + packageName);
- }
- return callingUid;
+ @Override
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
+ throws RemoteException {
+ getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
}
- void sendInitialBroadcasts() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.instances.size() > 0) {
- sendEnableIntentLocked(p);
- int[] appWidgetIds = getAppWidgetIds(p);
- sendUpdateIntentLocked(p, appWidgetIds);
- registerForBroadcastsLocked(p, appWidgetIds);
- }
- }
- }
+ @Override
+ public void stopListening(int hostId) throws RemoteException {
+ getImplForUser().stopListening(hostId);
}
- // only call from initialization -- it assumes that the data structures are all empty
- void loadStateLocked() {
- AtomicFile file = savedStateFile();
- try {
- FileInputStream stream = file.openRead();
- readStateFromFileLocked(stream);
-
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to close state FileInputStream " + e);
- }
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Failed to read state: " + e);
- }
+ @Override
+ public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
+ getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
}
- void saveStateLocked() {
- AtomicFile file = savedStateFile();
- FileOutputStream stream;
- try {
- stream = file.startWrite();
- if (writeStateToFileLocked(stream)) {
- file.finishWrite(stream);
- } else {
- file.failWrite(stream);
- Slog.w(TAG, "Failed to save state, restoring backup.");
- }
- } catch (IOException e) {
- Slog.w(TAG, "Failed open state file for write: " + e);
- }
+ @Override
+ public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
+ getImplForUser().updateAppWidgetIds(appWidgetIds, views);
}
- boolean writeStateToFileLocked(FileOutputStream stream) {
- int N;
-
- try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, "utf-8");
- out.startDocument(null, true);
- out.startTag(null, "gs");
-
- int providerIndex = 0;
- N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.instances.size() > 0) {
- out.startTag(null, "p");
- out.attribute(null, "pkg", p.info.provider.getPackageName());
- out.attribute(null, "cl", p.info.provider.getClassName());
- out.endTag(null, "p");
- p.tag = providerIndex;
- providerIndex++;
- }
- }
-
- N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host host = mHosts.get(i);
- out.startTag(null, "h");
- out.attribute(null, "pkg", host.packageName);
- out.attribute(null, "id", Integer.toHexString(host.hostId));
- out.endTag(null, "h");
- host.tag = i;
- }
-
- N = mAppWidgetIds.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = mAppWidgetIds.get(i);
- out.startTag(null, "g");
- out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
- out.attribute(null, "h", Integer.toHexString(id.host.tag));
- if (id.provider != null) {
- out.attribute(null, "p", Integer.toHexString(id.provider.tag));
- }
- out.endTag(null, "g");
- }
-
- out.endTag(null, "gs");
-
- out.endDocument();
- return true;
- } catch (IOException e) {
- Slog.w(TAG, "Failed to write state: " + e);
- return false;
- }
+ @Override
+ public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
+ throws RemoteException {
+ getImplForUser().updateAppWidgetProvider(provider, views);
}
- void readStateFromFileLocked(FileInputStream stream) {
- boolean success = false;
-
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, null);
-
- int type;
- int providerIndex = 0;
- HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
- do {
- type = parser.next();
- if (type == XmlPullParser.START_TAG) {
- String tag = parser.getName();
- if ("p".equals(tag)) {
- // TODO: do we need to check that this package has the same signature
- // as before?
- String pkg = parser.getAttributeValue(null, "pkg");
- String cl = parser.getAttributeValue(null, "cl");
-
- final PackageManager packageManager = mContext.getPackageManager();
- try {
- packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
- } catch (PackageManager.NameNotFoundException e) {
- String[] pkgs = packageManager.currentToCanonicalPackageNames(
- new String[] { pkg });
- pkg = pkgs[0];
- }
-
- Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
- if (p == null && mSafeMode) {
- // if we're in safe mode, make a temporary one
- p = new Provider();
- p.info = new AppWidgetProviderInfo();
- p.info.provider = new ComponentName(pkg, cl);
- p.zombie = true;
- mInstalledProviders.add(p);
- }
- if (p != null) {
- // if it wasn't uninstalled or something
- loadedProviders.put(providerIndex, p);
- }
- providerIndex++;
- }
- else if ("h".equals(tag)) {
- Host host = new Host();
-
- // TODO: do we need to check that this package has the same signature
- // as before?
- host.packageName = parser.getAttributeValue(null, "pkg");
- try {
- host.uid = getUidForPackage(host.packageName);
- } catch (PackageManager.NameNotFoundException ex) {
- host.zombie = true;
- }
- if (!host.zombie || mSafeMode) {
- // In safe mode, we don't discard the hosts we don't recognize
- // so that they're not pruned from our list. Otherwise, we do.
- host.hostId = Integer.parseInt(
- parser.getAttributeValue(null, "id"), 16);
- mHosts.add(host);
- }
- }
- else if ("g".equals(tag)) {
- AppWidgetId id = new AppWidgetId();
- id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
- if (id.appWidgetId >= mNextAppWidgetId) {
- mNextAppWidgetId = id.appWidgetId + 1;
- }
-
- String providerString = parser.getAttributeValue(null, "p");
- if (providerString != null) {
- // there's no provider if it hasn't been bound yet.
- // maybe we don't have to save this, but it brings the system
- // to the state it was in.
- int pIndex = Integer.parseInt(providerString, 16);
- id.provider = loadedProviders.get(pIndex);
- if (false) {
- Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
- + pIndex + " which is " + id.provider);
- }
- if (id.provider == null) {
- // This provider is gone. We just let the host figure out
- // that this happened when it fails to load it.
- continue;
- }
- }
-
- int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
- id.host = mHosts.get(hIndex);
- if (id.host == null) {
- // This host is gone.
- continue;
- }
-
- if (id.provider != null) {
- id.provider.instances.add(id);
- }
- id.host.instances.add(id);
- mAppWidgetIds.add(id);
- }
- }
- } while (type != XmlPullParser.END_DOCUMENT);
- success = true;
- } catch (NullPointerException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (IOException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (IndexOutOfBoundsException e) {
- Slog.w(TAG, "failed parsing " + e);
- }
-
- if (success) {
- // delete any hosts that didn't manage to get connected (should happen)
- // if it matters, they'll be reconnected.
- for (int i=mHosts.size()-1; i>=0; i--) {
- pruneHostLocked(mHosts.get(i));
- }
- } else {
- // failed reading, clean up
- Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
-
- mAppWidgetIds.clear();
- mHosts.clear();
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- mInstalledProviders.get(i).instances.clear();
- }
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ // Dump the state of all the app widget providers
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.dump(fd, pw, args);
}
}
- AtomicFile savedStateFile() {
- return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
- }
-
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- //Slog.d(TAG, "received " + action);
+ // Slog.d(TAG, "received " + action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- sendInitialBroadcasts();
+ getImplForUser().sendInitialBroadcasts();
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- Locale revised = Locale.getDefault();
- if (revised == null || mLocale == null ||
- !(revised.equals(mLocale))) {
- mLocale = revised;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- String pkgName = p.info.provider.getPackageName();
- updateProvidersForPackageLocked(pkgName);
- }
- saveStateLocked();
- }
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.onConfigurationChanged();
}
} else {
- boolean added = false;
- boolean changed = false;
- String pkgList[] = null;
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- added = true;
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- added = false;
- } else {
- Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
- String pkgName = uri.getSchemeSpecificPart();
- if (pkgName == null) {
- return;
- }
- pkgList = new String[] { pkgName };
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
- }
- if (pkgList == null || pkgList.length == 0) {
- return;
- }
- if (added || changed) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Bundle extras = intent.getExtras();
- if (changed || (extras != null &&
- extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
- for (String pkgName : pkgList) {
- // The package was just upgraded
- updateProvidersForPackageLocked(pkgName);
- }
- } else {
- // The package was just added
- for (String pkgName : pkgList) {
- addProvidersForPackageLocked(pkgName);
- }
- }
- saveStateLocked();
- }
- } else {
- Bundle extras = intent.getExtras();
- if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
- // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
- } else {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (String pkgName : pkgList) {
- removeProvidersForPackageLocked(pkgName);
- saveStateLocked();
- }
- }
- }
- }
+ // TODO: Verify that this only needs to be delivered for the related user and not
+ // all the users
+ getImplForUser().onBroadcastReceived(intent);
}
}
};
-
- void addProvidersForPackageLocked(String pkgName) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.setPackage(pkgName);
- List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- ActivityInfo ai = ri.activityInfo;
- if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- continue;
- }
- if (pkgName.equals(ai.packageName)) {
- addProviderLocked(ri);
- }
- }
- }
-
- void updateProvidersForPackageLocked(String pkgName) {
- HashSet<String> keep = new HashSet<String>();
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.setPackage(pkgName);
- List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- // add the missing ones and collect which ones to keep
- int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- ActivityInfo ai = ri.activityInfo;
- if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- continue;
- }
- if (pkgName.equals(ai.packageName)) {
- ComponentName component = new ComponentName(ai.packageName, ai.name);
- Provider p = lookupProviderLocked(component);
- if (p == null) {
- if (addProviderLocked(ri)) {
- keep.add(ai.name);
- }
- } else {
- Provider parsed = parseProviderInfoXml(component, ri);
- if (parsed != null) {
- keep.add(ai.name);
- // Use the new AppWidgetProviderInfo.
- p.info = parsed.info;
- // If it's enabled
- final int M = p.instances.size();
- if (M > 0) {
- int[] appWidgetIds = getAppWidgetIds(p);
- // Reschedule for the new updatePeriodMillis (don't worry about handling
- // it specially if updatePeriodMillis didn't change because we just sent
- // an update, and the next one will be updatePeriodMillis from now).
- cancelBroadcasts(p);
- registerForBroadcastsLocked(p, appWidgetIds);
- // If it's currently showing, call back with the new AppWidgetProviderInfo.
- for (int j=0; j<M; j++) {
- AppWidgetId id = p.instances.get(j);
- id.views = null;
- if (id.host != null && id.host.callbacks != null) {
- try {
- id.host.callbacks.providerChanged(id.appWidgetId, p.info);
- } catch (RemoteException ex) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this
- // instance.
- id.host.callbacks = null;
- }
- }
- }
- // Now that we've told the host, push out an update.
- sendUpdateIntentLocked(p, appWidgetIds);
- }
- }
- }
- }
- }
-
- // prune the ones we don't want to keep
- N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- if (pkgName.equals(p.info.provider.getPackageName())
- && !keep.contains(p.info.provider.getClassName())) {
- removeProviderLocked(i, p);
- }
- }
- }
-
- void removeProvidersForPackageLocked(String pkgName) {
- int N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- if (pkgName.equals(p.info.provider.getPackageName())) {
- removeProviderLocked(i, p);
- }
- }
-
- // Delete the hosts for this package too
- //
- // By now, we have removed any AppWidgets that were in any hosts here,
- // so we don't need to worry about sending DISABLE broadcasts to them.
- N = mHosts.size();
- for (int i=N-1; i>=0; i--) {
- Host host = mHosts.get(i);
- if (pkgName.equals(host.packageName)) {
- deleteHostLocked(host);
- }
- }
- }
}
-
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
new file mode 100644
index 0000000..250386f
--- /dev/null
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -0,0 +1,1606 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.Intent.FilterComparison;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserId;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.widget.RemoteViews;
+
+import com.android.internal.appwidget.IAppWidgetHost;
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.widget.IRemoteViewsAdapterConnection;
+import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.server.am.ActivityManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+class AppWidgetServiceImpl {
+
+ private static final String TAG = "AppWidgetServiceImpl";
+ private static final String SETTINGS_FILENAME = "appwidgets.xml";
+ private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
+
+ /*
+ * When identifying a Host or Provider based on the calling process, use the uid field. When
+ * identifying a Host or Provider based on a package manager broadcast, use the package given.
+ */
+
+ static class Provider {
+ int uid;
+ AppWidgetProviderInfo info;
+ ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+ PendingIntent broadcast;
+ boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+ int tag; // for use while saving state (the index)
+ }
+
+ static class Host {
+ int uid;
+ int hostId;
+ String packageName;
+ ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+ IAppWidgetHost callbacks;
+ boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+ int tag; // for use while saving state (the index)
+ }
+
+ static class AppWidgetId {
+ int appWidgetId;
+ Provider provider;
+ RemoteViews views;
+ Host host;
+ }
+
+ /**
+ * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
+ * needs to be a static inner class since a reference to the ServiceConnection is held globally
+ * and may lead us to leak AppWidgetService instances (if there were more than one).
+ */
+ static class ServiceConnectionProxy implements ServiceConnection {
+ private final IBinder mConnectionCb;
+
+ ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
+ mConnectionCb = connectionCb;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+ .asInterface(mConnectionCb);
+ try {
+ cb.onServiceConnected(service);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ disconnect();
+ }
+
+ public void disconnect() {
+ final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+ .asInterface(mConnectionCb);
+ try {
+ cb.onServiceDisconnected();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // Manages active connections to RemoteViewsServices
+ private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
+ // Manages persistent references to RemoteViewsServices from different App Widgets
+ private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
+
+ Context mContext;
+ Locale mLocale;
+ PackageManager mPackageManager;
+ AlarmManager mAlarmManager;
+ ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
+ int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
+ final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
+ ArrayList<Host> mHosts = new ArrayList<Host>();
+ boolean mSafeMode;
+ int mUserId;
+ boolean mStateLoaded;
+
+ // These are for debugging only -- widgets are going missing in some rare instances
+ ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
+ ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+ AppWidgetServiceImpl(Context context, int userId) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ mUserId = userId;
+ }
+
+ public void systemReady(boolean safeMode) {
+ mSafeMode = safeMode;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ }
+ }
+
+ void onConfigurationChanged() {
+ Locale revised = Locale.getDefault();
+ if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
+ mLocale = revised;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ String pkgName = p.info.provider.getPackageName();
+ updateProvidersForPackageLocked(pkgName);
+ }
+ saveStateLocked();
+ }
+ }
+ }
+
+ void onBroadcastReceived(Intent intent) {
+ final String action = intent.getAction();
+ boolean added = false;
+ boolean changed = false;
+ String pkgList[] = null;
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ added = true;
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ added = false;
+ } else {
+ Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ String pkgName = uri.getSchemeSpecificPart();
+ if (pkgName == null) {
+ return;
+ }
+ pkgList = new String[] { pkgName };
+ added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+ }
+ if (pkgList == null || pkgList.length == 0) {
+ return;
+ }
+ if (added || changed) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Bundle extras = intent.getExtras();
+ if (changed
+ || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
+ for (String pkgName : pkgList) {
+ // The package was just upgraded
+ updateProvidersForPackageLocked(pkgName);
+ }
+ } else {
+ // The package was just added
+ for (String pkgName : pkgList) {
+ addProvidersForPackageLocked(pkgName);
+ }
+ }
+ saveStateLocked();
+ }
+ } else {
+ Bundle extras = intent.getExtras();
+ if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
+ } else {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (String pkgName : pkgList) {
+ removeProvidersForPackageLocked(pkgName);
+ saveStateLocked();
+ }
+ }
+ }
+ }
+ }
+
+ private void dumpProvider(Provider p, int index, PrintWriter pw) {
+ AppWidgetProviderInfo info = p.info;
+ pw.print(" ["); pw.print(index); pw.print("] provider ");
+ pw.print(info.provider.flattenToShortString());
+ pw.println(':');
+ pw.print(" min=("); pw.print(info.minWidth);
+ pw.print("x"); pw.print(info.minHeight);
+ pw.print(") minResize=("); pw.print(info.minResizeWidth);
+ pw.print("x"); pw.print(info.minResizeHeight);
+ pw.print(") updatePeriodMillis=");
+ pw.print(info.updatePeriodMillis);
+ pw.print(" resizeMode=");
+ pw.print(info.resizeMode);
+ pw.print(" autoAdvanceViewId=");
+ pw.print(info.autoAdvanceViewId);
+ pw.print(" initialLayout=#");
+ pw.print(Integer.toHexString(info.initialLayout));
+ pw.print(" zombie="); pw.println(p.zombie);
+ }
+
+ private void dumpHost(Host host, int index, PrintWriter pw) {
+ pw.print(" ["); pw.print(index); pw.print("] hostId=");
+ pw.print(host.hostId); pw.print(' ');
+ pw.print(host.packageName); pw.print('/');
+ pw.print(host.uid); pw.println(':');
+ pw.print(" callbacks="); pw.println(host.callbacks);
+ pw.print(" instances.size="); pw.print(host.instances.size());
+ pw.print(" zombie="); pw.println(host.zombie);
+ }
+
+ private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
+ pw.print(" ["); pw.print(index); pw.print("] id=");
+ pw.println(id.appWidgetId);
+ pw.print(" hostId=");
+ pw.print(id.host.hostId); pw.print(' ');
+ pw.print(id.host.packageName); pw.print('/');
+ pw.println(id.host.uid);
+ if (id.provider != null) {
+ pw.print(" provider=");
+ pw.println(id.provider.info.provider.flattenToShortString());
+ }
+ if (id.host != null) {
+ pw.print(" host.callbacks="); pw.println(id.host.callbacks);
+ }
+ if (id.views != null) {
+ pw.print(" views="); pw.println(id.views);
+ }
+ }
+
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mAppWidgetIds) {
+ int N = mInstalledProviders.size();
+ pw.println("Providers:");
+ for (int i=0; i<N; i++) {
+ dumpProvider(mInstalledProviders.get(i), i, pw);
+ }
+
+ N = mAppWidgetIds.size();
+ pw.println(" ");
+ pw.println("AppWidgetIds:");
+ for (int i=0; i<N; i++) {
+ dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
+ }
+
+ N = mHosts.size();
+ pw.println(" ");
+ pw.println("Hosts:");
+ for (int i=0; i<N; i++) {
+ dumpHost(mHosts.get(i), i, pw);
+ }
+
+ N = mDeletedProviders.size();
+ pw.println(" ");
+ pw.println("Deleted Providers:");
+ for (int i=0; i<N; i++) {
+ dumpProvider(mDeletedProviders.get(i), i, pw);
+ }
+
+ N = mDeletedHosts.size();
+ pw.println(" ");
+ pw.println("Deleted Hosts:");
+ for (int i=0; i<N; i++) {
+ dumpHost(mDeletedHosts.get(i), i, pw);
+ }
+ }
+ }
+
+ private void ensureStateLoadedLocked() {
+ if (!mStateLoaded) {
+ loadAppWidgetList();
+ loadStateLocked();
+ mStateLoaded = true;
+ }
+ }
+
+ public int allocateAppWidgetId(String packageName, int hostId) {
+ int callingUid = enforceCallingUid(packageName);
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int appWidgetId = mNextAppWidgetId++;
+
+ Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+
+ AppWidgetId id = new AppWidgetId();
+ id.appWidgetId = appWidgetId;
+ id.host = host;
+
+ host.instances.add(id);
+ mAppWidgetIds.add(id);
+
+ saveStateLocked();
+
+ return appWidgetId;
+ }
+ }
+
+ public void deleteAppWidgetId(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null) {
+ deleteAppWidgetLocked(id);
+ saveStateLocked();
+ }
+ }
+ }
+
+ public void deleteHost(int hostId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int callingUid = Binder.getCallingUid();
+ Host host = lookupHostLocked(callingUid, hostId);
+ if (host != null) {
+ deleteHostLocked(host);
+ saveStateLocked();
+ }
+ }
+ }
+
+ public void deleteAllHosts() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int callingUid = Binder.getCallingUid();
+ final int N = mHosts.size();
+ boolean changed = false;
+ for (int i = N - 1; i >= 0; i--) {
+ Host host = mHosts.get(i);
+ if (host.uid == callingUid) {
+ deleteHostLocked(host);
+ changed = true;
+ }
+ }
+ if (changed) {
+ saveStateLocked();
+ }
+ }
+ }
+
+ void deleteHostLocked(Host host) {
+ final int N = host.instances.size();
+ for (int i = N - 1; i >= 0; i--) {
+ AppWidgetId id = host.instances.get(i);
+ deleteAppWidgetLocked(id);
+ }
+ host.instances.clear();
+ mHosts.remove(host);
+ mDeletedHosts.add(host);
+ // it's gone or going away, abruptly drop the callback connection
+ host.callbacks = null;
+ }
+
+ void deleteAppWidgetLocked(AppWidgetId id) {
+ // We first unbind all services that are bound to this id
+ unbindAppWidgetRemoteViewsServicesLocked(id);
+
+ Host host = id.host;
+ host.instances.remove(id);
+ pruneHostLocked(host);
+
+ mAppWidgetIds.remove(id);
+
+ Provider p = id.provider;
+ if (p != null) {
+ p.instances.remove(id);
+ if (!p.zombie) {
+ // send the broacast saying that this appWidgetId has been deleted
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
+ intent.setComponent(p.info.provider);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
+ mContext.sendBroadcast(intent);
+ if (p.instances.size() == 0) {
+ // cancel the future updates
+ cancelBroadcasts(p);
+
+ // send the broacast saying that the provider is not in use any more
+ intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+ }
+ }
+ }
+
+ void cancelBroadcasts(Provider p) {
+ if (p.broadcast != null) {
+ mAlarmManager.cancel(p.broadcast);
+ long token = Binder.clearCallingIdentity();
+ try {
+ p.broadcast.cancel();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ p.broadcast = null;
+ }
+ }
+
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
+ "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+ if (id.provider != null) {
+ throw new IllegalArgumentException("appWidgetId " + appWidgetId
+ + " already bound to " + id.provider.info.provider);
+ }
+ Provider p = lookupProviderLocked(provider);
+ if (p == null) {
+ throw new IllegalArgumentException("not a appwidget provider: " + provider);
+ }
+ if (p.zombie) {
+ throw new IllegalArgumentException("can't bind to a 3rd party provider in"
+ + " safe mode: " + provider);
+ }
+
+ Binder.restoreCallingIdentity(ident);
+
+ id.provider = p;
+ p.instances.add(id);
+ int instancesSize = p.instances.size();
+ if (instancesSize == 1) {
+ // tell the provider that it's ready
+ sendEnableIntentLocked(p);
+ }
+
+ // send an update now -- We need this update now, and just for this appWidgetId.
+ // It's less critical when the next one happens, so when we schedule the next one,
+ // we add updatePeriodMillis to its start time. That time will have some slop,
+ // but that's okay.
+ sendUpdateIntentLocked(p, new int[] { appWidgetId });
+
+ // schedule the future updates
+ registerForBroadcastsLocked(p, getAppWidgetIds(p));
+ saveStateLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Binds to a specific RemoteViewsService
+ public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+ final ComponentName componentName = intent.getComponent();
+ try {
+ final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
+ PackageManager.GET_PERMISSIONS);
+ if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
+ throw new SecurityException("Selected service does not require "
+ + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown component " + componentName);
+ }
+
+ // If there is already a connection made for this service intent, then disconnect from
+ // that first. (This does not allow multiple connections to the same service under
+ // the same key)
+ ServiceConnectionProxy conn = null;
+ FilterComparison fc = new FilterComparison(intent);
+ Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
+ if (mBoundRemoteViewsServices.containsKey(key)) {
+ conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ mBoundRemoteViewsServices.remove(key);
+ }
+
+ // Bind to the RemoteViewsService (which will trigger a callback to the
+ // RemoteViewsAdapter.onServiceConnected())
+ final long token = Binder.clearCallingIdentity();
+ try {
+ conn = new ServiceConnectionProxy(key, connection);
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ mBoundRemoteViewsServices.put(key, conn);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
+ // when we can call back to the RemoteViewsService later to destroy associated
+ // factories.
+ incrementAppWidgetServiceRefCount(appWidgetId, fc);
+ }
+ }
+
+ // Unbinds from a specific RemoteViewsService
+ public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ // Unbind from the RemoteViewsService (which will trigger a callback to the bound
+ // RemoteViewsAdapter)
+ Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
+ intent));
+ if (mBoundRemoteViewsServices.containsKey(key)) {
+ // We don't need to use the appWidgetId until after we are sure there is something
+ // to unbind. Note that this may mask certain issues with apps calling unbind()
+ // more than necessary.
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+
+ ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+ .get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ mBoundRemoteViewsServices.remove(key);
+ } else {
+ Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
+ }
+ }
+ }
+
+ // Unbinds from a RemoteViewsService when we delete an app widget
+ private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
+ int appWidgetId = id.appWidgetId;
+ // Unbind all connections to Services bound to this AppWidgetId
+ Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
+ .iterator();
+ while (it.hasNext()) {
+ final Pair<Integer, Intent.FilterComparison> key = it.next();
+ if (key.first.intValue() == appWidgetId) {
+ final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+ .get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ it.remove();
+ }
+ }
+
+ // Check if we need to destroy any services (if no other app widgets are
+ // referencing the same service)
+ decrementAppWidgetServiceRefCount(appWidgetId);
+ }
+
+ // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
+ private void destroyRemoteViewsService(final Intent intent) {
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
+ try {
+ cb.onDestroy(intent);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ mContext.unbindService(this);
+ }
+
+ @Override
+ public void onServiceDisconnected(android.content.ComponentName name) {
+ // Do nothing
+ }
+ };
+
+ // Bind to the service and remove the static intent->factory mapping in the
+ // RemoteViewsService.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Adds to the ref-count for a given RemoteViewsService intent
+ private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
+ HashSet<Integer> appWidgetIds = null;
+ if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
+ appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
+ } else {
+ appWidgetIds = new HashSet<Integer>();
+ mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
+ }
+ appWidgetIds.add(appWidgetId);
+ }
+
+ // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
+ // the ref-count reaches zero.
+ private void decrementAppWidgetServiceRefCount(int appWidgetId) {
+ Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
+ while (it.hasNext()) {
+ final FilterComparison key = it.next();
+ final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
+ if (ids.remove(appWidgetId)) {
+ // If we have removed the last app widget referencing this service, then we
+ // should destroy it and remove it from this set
+ if (ids.isEmpty()) {
+ destroyRemoteViewsService(key.getIntent());
+ it.remove();
+ }
+ }
+ }
+ }
+
+ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null && id.provider != null && !id.provider.zombie) {
+ return id.provider.info;
+ }
+ return null;
+ }
+ }
+
+ public RemoteViews getAppWidgetViews(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null) {
+ return id.views;
+ }
+ return null;
+ }
+ }
+
+ public List<AppWidgetProviderInfo> getInstalledProviders() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ final int N = mInstalledProviders.size();
+ ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (!p.zombie) {
+ result.add(p.info);
+ }
+ }
+ return result;
+ }
+ }
+
+ public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ updateAppWidgetInstanceLocked(id, views);
+ }
+ }
+ }
+
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ updateAppWidgetInstanceLocked(id, views, true);
+ }
+ }
+ }
+
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
+ }
+ }
+ }
+
+ public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Provider p = lookupProviderLocked(provider);
+ if (p == null) {
+ Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
+ return;
+ }
+ ArrayList<AppWidgetId> instances = p.instances;
+ final int callingUid = Binder.getCallingUid();
+ final int N = instances.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = instances.get(i);
+ if (canAccessAppWidgetId(id, callingUid)) {
+ updateAppWidgetInstanceLocked(id, views);
+ }
+ }
+ }
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+ updateAppWidgetInstanceLocked(id, views, false);
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
+ // allow for stale appWidgetIds and other badness
+ // lookup also checks that the calling process can access the appWidgetId
+ // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+ if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+
+ // We do not want to save this RemoteViews
+ if (!isPartialUpdate)
+ id.views = views;
+
+ // is anyone listening?
+ if (id.host.callbacks != null) {
+ try {
+ // the lock is held, but this is a oneway call
+ id.host.callbacks.updateAppWidget(id.appWidgetId, views);
+ } catch (RemoteException e) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this instance.
+ id.host.callbacks = null;
+ }
+ }
+ }
+ }
+
+ void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
+ // allow for stale appWidgetIds and other badness
+ // lookup also checks that the calling process can access the appWidgetId
+ // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+ if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+ // is anyone listening?
+ if (id.host.callbacks != null) {
+ try {
+ // the lock is held, but this is a oneway call
+ id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
+ } catch (RemoteException e) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this instance.
+ id.host.callbacks = null;
+ }
+ }
+
+ // If the host is unavailable, then we call the associated
+ // RemoteViewsFactory.onDataSetChanged() directly
+ if (id.host.callbacks == null) {
+ Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
+ for (FilterComparison key : keys) {
+ if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
+ Intent intent = key.getIntent();
+
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
+ .asInterface(service);
+ try {
+ cb.onDataSetChangedAsync();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ mContext.unbindService(this);
+ }
+
+ @Override
+ public void onServiceDisconnected(android.content.ComponentName name) {
+ // Do nothing
+ }
+ };
+
+ // Bind to the service and call onDataSetChanged()
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
+ List<RemoteViews> updatedViews) {
+ int callingUid = enforceCallingUid(packageName);
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+ host.callbacks = callbacks;
+
+ updatedViews.clear();
+
+ ArrayList<AppWidgetId> instances = host.instances;
+ int N = instances.size();
+ int[] updatedIds = new int[N];
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = instances.get(i);
+ updatedIds[i] = id.appWidgetId;
+ updatedViews.add(id.views);
+ }
+ return updatedIds;
+ }
+ }
+
+ public void stopListening(int hostId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
+ if (host != null) {
+ host.callbacks = null;
+ pruneHostLocked(host);
+ }
+ }
+ }
+
+ boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
+ if (id.host.uid == callingUid) {
+ // Apps hosting the AppWidget have access to it.
+ return true;
+ }
+ if (id.provider != null && id.provider.uid == callingUid) {
+ // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
+ return true;
+ }
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
+ // Apps that can bind have access to all appWidgetIds.
+ return true;
+ }
+ // Nobody else can access it.
+ return false;
+ }
+
+ AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
+ int callingUid = Binder.getCallingUid();
+ final int N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
+ return id;
+ }
+ }
+ return null;
+ }
+
+ Provider lookupProviderLocked(ComponentName provider) {
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.info.provider.equals(provider)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ Host lookupHostLocked(int uid, int hostId) {
+ final int N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host h = mHosts.get(i);
+ if (h.uid == uid && h.hostId == hostId) {
+ return h;
+ }
+ }
+ return null;
+ }
+
+ Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
+ final int N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host h = mHosts.get(i);
+ if (h.hostId == hostId && h.packageName.equals(packageName)) {
+ return h;
+ }
+ }
+ Host host = new Host();
+ host.packageName = packageName;
+ host.uid = uid;
+ host.hostId = hostId;
+ mHosts.add(host);
+ return host;
+ }
+
+ void pruneHostLocked(Host host) {
+ if (host.instances.size() == 0 && host.callbacks == null) {
+ mHosts.remove(host);
+ }
+ }
+
+ void loadAppWidgetList() {
+ PackageManager pm = mPackageManager;
+
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ addProviderLocked(ri);
+ }
+ }
+
+ boolean addProviderLocked(ResolveInfo ri) {
+ if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ return false;
+ }
+ if (!ri.activityInfo.isEnabled()) {
+ return false;
+ }
+ Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
+ ri.activityInfo.name), ri);
+ if (p != null) {
+ mInstalledProviders.add(p);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void removeProviderLocked(int index, Provider p) {
+ int N = p.instances.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = p.instances.get(i);
+ // Call back with empty RemoteViews
+ updateAppWidgetInstanceLocked(id, null);
+ // Stop telling the host about updates for this from now on
+ cancelBroadcasts(p);
+ // clear out references to this appWidgetId
+ id.host.instances.remove(id);
+ mAppWidgetIds.remove(id);
+ id.provider = null;
+ pruneHostLocked(id.host);
+ id.host = null;
+ }
+ p.instances.clear();
+ mInstalledProviders.remove(index);
+ mDeletedProviders.add(p);
+ // no need to send the DISABLE broadcast, since the receiver is gone anyway
+ cancelBroadcasts(p);
+ }
+
+ void sendEnableIntentLocked(Provider p) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+
+ void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
+ if (appWidgetIds != null && appWidgetIds.length > 0) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+ }
+
+ void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
+ if (p.info.updatePeriodMillis > 0) {
+ // if this is the first instance, set the alarm. otherwise,
+ // rely on the fact that we've already set it and that
+ // PendingIntent.getBroadcast will update the extras.
+ boolean alreadyRegistered = p.broadcast != null;
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+ intent.setComponent(p.info.provider);
+ long token = Binder.clearCallingIdentity();
+ try {
+ p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (!alreadyRegistered) {
+ long period = p.info.updatePeriodMillis;
+ if (period < MIN_UPDATE_PERIOD) {
+ period = MIN_UPDATE_PERIOD;
+ }
+ mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
+ .elapsedRealtime()
+ + period, period, p.broadcast);
+ }
+ }
+ }
+
+ static int[] getAppWidgetIds(Provider p) {
+ int instancesSize = p.instances.size();
+ int appWidgetIds[] = new int[instancesSize];
+ for (int i = 0; i < instancesSize; i++) {
+ appWidgetIds[i] = p.instances.get(i).appWidgetId;
+ }
+ return appWidgetIds;
+ }
+
+ public int[] getAppWidgetIds(ComponentName provider) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Provider p = lookupProviderLocked(provider);
+ if (p != null && Binder.getCallingUid() == p.uid) {
+ return getAppWidgetIds(p);
+ } else {
+ return new int[0];
+ }
+ }
+ }
+
+ private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
+ Provider p = null;
+
+ ActivityInfo activityInfo = ri.activityInfo;
+ XmlResourceParser parser = null;
+ try {
+ parser = activityInfo.loadXmlMetaData(mPackageManager,
+ AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
+ if (parser == null) {
+ Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
+ + " meta-data for " + "AppWidget provider '" + component + '\'');
+ return null;
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // drain whitespace, comments, etc.
+ }
+
+ String nodeName = parser.getName();
+ if (!"appwidget-provider".equals(nodeName)) {
+ Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
+ + " AppWidget provider '" + component + '\'');
+ return null;
+ }
+
+ p = new Provider();
+ AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
+ info.provider = component;
+ p.uid = activityInfo.applicationInfo.uid;
+
+ Resources res = mPackageManager
+ .getResourcesForApplication(activityInfo.applicationInfo);
+
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AppWidgetProviderInfo);
+
+ // These dimensions has to be resolved in the application's context.
+ // We simply send back the raw complex data, which will be
+ // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
+ TypedValue value = sa
+ .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
+ info.minWidth = value != null ? value.data : 0;
+ value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
+ info.minHeight = value != null ? value.data : 0;
+ value = sa.peekValue(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
+ info.minResizeWidth = value != null ? value.data : info.minWidth;
+ value = sa.peekValue(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
+ info.minResizeHeight = value != null ? value.data : info.minHeight;
+ info.updatePeriodMillis = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
+ info.initialLayout = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
+ String className = sa
+ .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
+ if (className != null) {
+ info.configure = new ComponentName(component.getPackageName(), className);
+ }
+ info.label = activityInfo.loadLabel(mPackageManager).toString();
+ info.icon = ri.getIconResource();
+ info.previewImage = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+ info.autoAdvanceViewId = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
+ info.resizeMode = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
+ AppWidgetProviderInfo.RESIZE_NONE);
+
+ sa.recycle();
+ } catch (Exception e) {
+ // Ok to catch Exception here, because anything going wrong because
+ // of what a client process passes to us should not be fatal for the
+ // system process.
+ Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
+ return null;
+ } finally {
+ if (parser != null)
+ parser.close();
+ }
+ return p;
+ }
+
+ int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
+ PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
+ if (pkgInfo == null || pkgInfo.applicationInfo == null) {
+ throw new PackageManager.NameNotFoundException();
+ }
+ return pkgInfo.applicationInfo.uid;
+ }
+
+ int enforceCallingUid(String packageName) throws IllegalArgumentException {
+ int callingUid = Binder.getCallingUid();
+ int packageUid;
+ try {
+ packageUid = getUidForPackage(packageName);
+ } catch (PackageManager.NameNotFoundException ex) {
+ throw new IllegalArgumentException("packageName and uid don't match packageName="
+ + packageName);
+ }
+ if (!UserId.isSameApp(callingUid, packageUid)) {
+ throw new IllegalArgumentException("packageName and uid don't match packageName="
+ + packageName);
+ }
+ return callingUid;
+ }
+
+ void sendInitialBroadcasts() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.instances.size() > 0) {
+ sendEnableIntentLocked(p);
+ int[] appWidgetIds = getAppWidgetIds(p);
+ sendUpdateIntentLocked(p, appWidgetIds);
+ registerForBroadcastsLocked(p, appWidgetIds);
+ }
+ }
+ }
+ }
+
+ // only call from initialization -- it assumes that the data structures are all empty
+ void loadStateLocked() {
+ AtomicFile file = savedStateFile();
+ try {
+ FileInputStream stream = file.openRead();
+ readStateFromFileLocked(stream);
+
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close state FileInputStream " + e);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Failed to read state: " + e);
+ }
+ }
+
+ void saveStateLocked() {
+ AtomicFile file = savedStateFile();
+ FileOutputStream stream;
+ try {
+ stream = file.startWrite();
+ if (writeStateToFileLocked(stream)) {
+ file.finishWrite(stream);
+ } else {
+ file.failWrite(stream);
+ Slog.w(TAG, "Failed to save state, restoring backup.");
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed open state file for write: " + e);
+ }
+ }
+
+ boolean writeStateToFileLocked(FileOutputStream stream) {
+ int N;
+
+ try {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, "gs");
+
+ int providerIndex = 0;
+ N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.instances.size() > 0) {
+ out.startTag(null, "p");
+ out.attribute(null, "pkg", p.info.provider.getPackageName());
+ out.attribute(null, "cl", p.info.provider.getClassName());
+ out.endTag(null, "p");
+ p.tag = providerIndex;
+ providerIndex++;
+ }
+ }
+
+ N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host host = mHosts.get(i);
+ out.startTag(null, "h");
+ out.attribute(null, "pkg", host.packageName);
+ out.attribute(null, "id", Integer.toHexString(host.hostId));
+ out.endTag(null, "h");
+ host.tag = i;
+ }
+
+ N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ out.startTag(null, "g");
+ out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
+ out.attribute(null, "h", Integer.toHexString(id.host.tag));
+ if (id.provider != null) {
+ out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+ }
+ out.endTag(null, "g");
+ }
+
+ out.endTag(null, "gs");
+
+ out.endDocument();
+ return true;
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to write state: " + e);
+ return false;
+ }
+ }
+
+ void readStateFromFileLocked(FileInputStream stream) {
+ boolean success = false;
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type;
+ int providerIndex = 0;
+ HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if ("p".equals(tag)) {
+ // TODO: do we need to check that this package has the same signature
+ // as before?
+ String pkg = parser.getAttributeValue(null, "pkg");
+ String cl = parser.getAttributeValue(null, "cl");
+
+ final PackageManager packageManager = mContext.getPackageManager();
+ try {
+ packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ String[] pkgs = packageManager
+ .currentToCanonicalPackageNames(new String[] { pkg });
+ pkg = pkgs[0];
+ }
+
+ Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
+ if (p == null && mSafeMode) {
+ // if we're in safe mode, make a temporary one
+ p = new Provider();
+ p.info = new AppWidgetProviderInfo();
+ p.info.provider = new ComponentName(pkg, cl);
+ p.zombie = true;
+ mInstalledProviders.add(p);
+ }
+ if (p != null) {
+ // if it wasn't uninstalled or something
+ loadedProviders.put(providerIndex, p);
+ }
+ providerIndex++;
+ } else if ("h".equals(tag)) {
+ Host host = new Host();
+
+ // TODO: do we need to check that this package has the same signature
+ // as before?
+ host.packageName = parser.getAttributeValue(null, "pkg");
+ try {
+ host.uid = getUidForPackage(host.packageName);
+ } catch (PackageManager.NameNotFoundException ex) {
+ host.zombie = true;
+ }
+ if (!host.zombie || mSafeMode) {
+ // In safe mode, we don't discard the hosts we don't recognize
+ // so that they're not pruned from our list. Otherwise, we do.
+ host.hostId = Integer
+ .parseInt(parser.getAttributeValue(null, "id"), 16);
+ mHosts.add(host);
+ }
+ } else if ("g".equals(tag)) {
+ AppWidgetId id = new AppWidgetId();
+ id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
+ if (id.appWidgetId >= mNextAppWidgetId) {
+ mNextAppWidgetId = id.appWidgetId + 1;
+ }
+
+ String providerString = parser.getAttributeValue(null, "p");
+ if (providerString != null) {
+ // there's no provider if it hasn't been bound yet.
+ // maybe we don't have to save this, but it brings the system
+ // to the state it was in.
+ int pIndex = Integer.parseInt(providerString, 16);
+ id.provider = loadedProviders.get(pIndex);
+ if (false) {
+ Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
+ + pIndex + " which is " + id.provider);
+ }
+ if (id.provider == null) {
+ // This provider is gone. We just let the host figure out
+ // that this happened when it fails to load it.
+ continue;
+ }
+ }
+
+ int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
+ id.host = mHosts.get(hIndex);
+ if (id.host == null) {
+ // This host is gone.
+ continue;
+ }
+
+ if (id.provider != null) {
+ id.provider.instances.add(id);
+ }
+ id.host.instances.add(id);
+ mAppWidgetIds.add(id);
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ }
+
+ if (success) {
+ // delete any hosts that didn't manage to get connected (should happen)
+ // if it matters, they'll be reconnected.
+ for (int i = mHosts.size() - 1; i >= 0; i--) {
+ pruneHostLocked(mHosts.get(i));
+ }
+ } else {
+ // failed reading, clean up
+ Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
+
+ mAppWidgetIds.clear();
+ mHosts.clear();
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ mInstalledProviders.get(i).instances.clear();
+ }
+ }
+ }
+
+ AtomicFile savedStateFile() {
+ int userId = Binder.getOrigCallingUser();
+ File dir = new File("/data/system/users/" + userId);
+ File settingsFile = new File(dir, SETTINGS_FILENAME);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ if (userId == 0) {
+ // Migrate old data
+ File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
+ // Method doesn't throw an exception on failure. Ignore any errors
+ // in moving the file (like non-existence)
+ oldFile.renameTo(settingsFile);
+ }
+ }
+ return new AtomicFile(settingsFile);
+ }
+
+ void addProvidersForPackageLocked(String pkgName) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.setPackage(pkgName);
+ List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ ActivityInfo ai = ri.activityInfo;
+ if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ continue;
+ }
+ if (pkgName.equals(ai.packageName)) {
+ addProviderLocked(ri);
+ }
+ }
+ }
+
+ void updateProvidersForPackageLocked(String pkgName) {
+ HashSet<String> keep = new HashSet<String>();
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.setPackage(pkgName);
+ List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ // add the missing ones and collect which ones to keep
+ int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ ActivityInfo ai = ri.activityInfo;
+ if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ continue;
+ }
+ if (pkgName.equals(ai.packageName)) {
+ ComponentName component = new ComponentName(ai.packageName, ai.name);
+ Provider p = lookupProviderLocked(component);
+ if (p == null) {
+ if (addProviderLocked(ri)) {
+ keep.add(ai.name);
+ }
+ } else {
+ Provider parsed = parseProviderInfoXml(component, ri);
+ if (parsed != null) {
+ keep.add(ai.name);
+ // Use the new AppWidgetProviderInfo.
+ p.info = parsed.info;
+ // If it's enabled
+ final int M = p.instances.size();
+ if (M > 0) {
+ int[] appWidgetIds = getAppWidgetIds(p);
+ // Reschedule for the new updatePeriodMillis (don't worry about handling
+ // it specially if updatePeriodMillis didn't change because we just sent
+ // an update, and the next one will be updatePeriodMillis from now).
+ cancelBroadcasts(p);
+ registerForBroadcastsLocked(p, appWidgetIds);
+ // If it's currently showing, call back with the new
+ // AppWidgetProviderInfo.
+ for (int j = 0; j < M; j++) {
+ AppWidgetId id = p.instances.get(j);
+ id.views = null;
+ if (id.host != null && id.host.callbacks != null) {
+ try {
+ id.host.callbacks.providerChanged(id.appWidgetId, p.info);
+ } catch (RemoteException ex) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this
+ // instance.
+ id.host.callbacks = null;
+ }
+ }
+ }
+ // Now that we've told the host, push out an update.
+ sendUpdateIntentLocked(p, appWidgetIds);
+ }
+ }
+ }
+ }
+ }
+
+ // prune the ones we don't want to keep
+ N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ if (pkgName.equals(p.info.provider.getPackageName())
+ && !keep.contains(p.info.provider.getClassName())) {
+ removeProviderLocked(i, p);
+ }
+ }
+ }
+
+ void removeProvidersForPackageLocked(String pkgName) {
+ int N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ if (pkgName.equals(p.info.provider.getPackageName())) {
+ removeProviderLocked(i, p);
+ }
+ }
+
+ // Delete the hosts for this package too
+ //
+ // By now, we have removed any AppWidgets that were in any hosts here,
+ // so we don't need to worry about sending DISABLE broadcasts to them.
+ N = mHosts.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Host host = mHosts.get(i);
+ if (pkgName.equals(host.packageName)) {
+ deleteHostLocked(host);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 4d5e0a6..a7b08f5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -140,6 +140,8 @@ class BackupManagerService extends IBackupManager.Stub {
static final int BACKUP_FILE_VERSION = 1;
static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
+ static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
+
// How often we perform a backup pass. Privileged external callers can
// trigger an immediate pass.
private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
@@ -235,6 +237,10 @@ class BackupManagerService extends IBackupManager.Stub {
volatile long mLastBackupPass;
volatile long mNextBackupPass;
+ // For debugging, we maintain a progress trace of operations during backup
+ static final boolean DEBUG_BACKUP_TRACE = true;
+ final List<String> mBackupTrace = new ArrayList<String>();
+
// A similar synchronization mechanism around clearing apps' data for restore
final Object mClearDataLock = new Object();
volatile boolean mClearingData;
@@ -652,6 +658,23 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // ----- Debug-only backup operation trace -----
+ void addBackupTrace(String s) {
+ if (DEBUG_BACKUP_TRACE) {
+ synchronized (mBackupTrace) {
+ mBackupTrace.add(s);
+ }
+ }
+ }
+
+ void clearBackupTrace() {
+ if (DEBUG_BACKUP_TRACE) {
+ synchronized (mBackupTrace) {
+ mBackupTrace.clear();
+ }
+ }
+ }
+
// ----- Main service implementation -----
public BackupManagerService(Context context) {
@@ -1612,6 +1635,7 @@ class BackupManagerService extends IBackupManager.Stub {
mAgentConnectLock.wait(5000);
} catch (InterruptedException e) {
// just bail
+ if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
return null;
}
}
@@ -1621,6 +1645,7 @@ class BackupManagerService extends IBackupManager.Stub {
Slog.w(TAG, "Timeout waiting for agent " + app);
return null;
}
+ if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
agent = mConnectedAgent;
}
} catch (RemoteException e) {
@@ -1650,7 +1675,8 @@ class BackupManagerService extends IBackupManager.Stub {
synchronized(mClearDataLock) {
mClearingData = true;
try {
- mActivityManager.clearApplicationUserData(packageName, observer);
+ mActivityManager.clearApplicationUserData(packageName, observer,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
@@ -1814,6 +1840,8 @@ class BackupManagerService extends IBackupManager.Stub {
mCurrentState = BackupState.INITIAL;
mFinished = false;
+
+ addBackupTrace("STATE => INITIAL");
}
// Main entry point: perform one chunk of work, updating the state as appropriate
@@ -1842,11 +1870,25 @@ class BackupManagerService extends IBackupManager.Stub {
// We're starting a backup pass. Initialize the transport and send
// the PM metadata blob if we haven't already.
void beginBackup() {
+ if (DEBUG_BACKUP_TRACE) {
+ clearBackupTrace();
+ StringBuilder b = new StringBuilder(256);
+ b.append("beginBackup: [");
+ for (BackupRequest req : mOriginalQueue) {
+ b.append(' ');
+ b.append(req.packageName);
+ }
+ b.append(" ]");
+ addBackupTrace(b.toString());
+ }
+
mStatus = BackupConstants.TRANSPORT_OK;
// Sanity check: if the queue is empty we have no work to do.
if (mOriginalQueue.isEmpty()) {
Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
+ addBackupTrace("queue empty at begin");
+ executeNextState(BackupState.FINAL);
return;
}
@@ -1859,13 +1901,17 @@ class BackupManagerService extends IBackupManager.Stub {
File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
try {
- EventLog.writeEvent(EventLogTags.BACKUP_START, mTransport.transportDirName());
+ final String transportName = mTransport.transportDirName();
+ EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
// If we haven't stored package manager metadata yet, we must init the transport.
if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
+ addBackupTrace("initializing transport " + transportName);
resetBackupState(mStateDir); // Just to make sure.
mStatus = mTransport.initializeDevice();
+
+ addBackupTrace("transport.initializeDevice() == " + mStatus);
if (mStatus == BackupConstants.TRANSPORT_OK) {
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
} else {
@@ -1884,6 +1930,7 @@ class BackupManagerService extends IBackupManager.Stub {
mPackageManager, allAgentPackages());
mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
+ addBackupTrace("PMBA invoke: " + mStatus);
}
if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
@@ -1894,11 +1941,13 @@ class BackupManagerService extends IBackupManager.Stub {
}
} catch (Exception e) {
Slog.e(TAG, "Error in backup thread", e);
+ addBackupTrace("Exception in backup thread: " + e);
mStatus = BackupConstants.TRANSPORT_ERROR;
} finally {
// If we've succeeded so far, invokeAgentForBackup() will have run the PM
// metadata and its completion/timeout callback will continue the state
// machine chain. If it failed that won't happen; we handle that now.
+ addBackupTrace("exiting prelim: " + mStatus);
if (mStatus != BackupConstants.TRANSPORT_OK) {
// if things went wrong at this point, we need to
// restage everything and try again later.
@@ -1912,11 +1961,12 @@ class BackupManagerService extends IBackupManager.Stub {
// if that was warranted. Now we process the single next thing in the queue.
void invokeNextAgent() {
mStatus = BackupConstants.TRANSPORT_OK;
+ addBackupTrace("invoke q=" + mQueue.size());
// Sanity check that we have work to do. If not, skip to the end where
// we reestablish the wakelock invariants etc.
if (mQueue.isEmpty()) {
- Slog.e(TAG, "Running queue but it's empty!");
+ if (DEBUG) Slog.i(TAG, "queue now empty");
executeNextState(BackupState.FINAL);
return;
}
@@ -1926,6 +1976,7 @@ class BackupManagerService extends IBackupManager.Stub {
mQueue.remove(0);
Slog.d(TAG, "starting agent for backup of " + request);
+ addBackupTrace("launch agent for " + request.packageName);
// Verify that the requested app exists; it might be something that
// requested a backup but was then uninstalled. The request was
@@ -1941,6 +1992,7 @@ class BackupManagerService extends IBackupManager.Stub {
mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
IApplicationThread.BACKUP_MODE_INCREMENTAL);
+ addBackupTrace("agent bound; a? = " + (agent != null));
if (agent != null) {
mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
// at this point we'll either get a completion callback from the
@@ -1954,14 +2006,17 @@ class BackupManagerService extends IBackupManager.Stub {
// Try for the next one.
Slog.d(TAG, "error in bind/backup", ex);
mStatus = BackupConstants.AGENT_ERROR;
+ addBackupTrace("agent SE");
}
} catch (NameNotFoundException e) {
Slog.d(TAG, "Package does not exist; skipping");
+ addBackupTrace("no such package");
+ mStatus = BackupConstants.AGENT_UNKNOWN;
} finally {
mWakelock.setWorkSource(null);
// If there was an agent error, no timeout/completion handling will occur.
- // That means we need to deal with the next state ourselves.
+ // That means we need to direct to the next state ourselves.
if (mStatus != BackupConstants.TRANSPORT_OK) {
BackupState nextState = BackupState.RUNNING_QUEUE;
@@ -1973,18 +2028,26 @@ class BackupManagerService extends IBackupManager.Stub {
dataChangedImpl(request.packageName);
mStatus = BackupConstants.TRANSPORT_OK;
if (mQueue.isEmpty()) nextState = BackupState.FINAL;
- } else if (mStatus != BackupConstants.TRANSPORT_OK) {
+ } else if (mStatus == BackupConstants.AGENT_UNKNOWN) {
+ // Failed lookup of the app, so we couldn't bring up an agent, but
+ // we're otherwise fine. Just drop it and go on to the next as usual.
+ mStatus = BackupConstants.TRANSPORT_OK;
+ } else {
// Transport-level failure means we reenqueue everything
revertAndEndBackup();
nextState = BackupState.FINAL;
}
executeNextState(nextState);
+ } else {
+ addBackupTrace("expecting completion/timeout callback");
}
}
}
void finalizeBackup() {
+ addBackupTrace("finishing");
+
// Either backup was successful, in which case we of course do not need
// this pass's journal any more; or it failed, in which case we just
// re-enqueued all of these packages in the current active journal.
@@ -1997,6 +2060,7 @@ class BackupManagerService extends IBackupManager.Stub {
// done a backup, we can now record what the current backup dataset token
// is.
if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) {
+ addBackupTrace("success; recording token");
try {
mCurrentToken = mTransport.getCurrentRestoreSet();
} catch (RemoteException e) {} // can't happen
@@ -2012,11 +2076,13 @@ class BackupManagerService extends IBackupManager.Stub {
// Make sure we back up everything and perform the one-time init
clearMetadata();
if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
+ addBackupTrace("init required; rerunning");
backupNow();
}
}
// Only once we're entirely finished do we release the wakelock
+ clearBackupTrace();
Slog.i(TAG, "Backup pass finished.");
mWakelock.release();
}
@@ -2031,7 +2097,8 @@ class BackupManagerService extends IBackupManager.Stub {
// handler in case it doesn't get back to us.
int invokeAgentForBackup(String packageName, IBackupAgent agent,
IBackupTransport transport) {
- if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName);
+ if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
+ addBackupTrace("invoking " + packageName);
mSavedStateName = new File(mStateDir, packageName);
mBackupDataName = new File(mDataDir, packageName + ".data");
@@ -2071,10 +2138,13 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor.MODE_TRUNCATE);
// Initiate the target's backup pass
+ addBackupTrace("setting timeout");
prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
+ addBackupTrace("calling agent doBackup()");
agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
} catch (Exception e) {
Slog.e(TAG, "Error invoking for backup on " + packageName);
+ addBackupTrace("exception: " + e);
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
e.toString());
agentErrorCleanup();
@@ -2085,6 +2155,7 @@ class BackupManagerService extends IBackupManager.Stub {
// either be a callback from the agent, at which point we'll process its data
// for transport, or a timeout. Either way the next phase will happen in
// response to the TimeoutHandler interface callbacks.
+ addBackupTrace("invoke success");
return BackupConstants.TRANSPORT_OK;
}
@@ -2096,6 +2167,7 @@ class BackupManagerService extends IBackupManager.Stub {
+ mCurrentPackage.packageName);
mBackupHandler.removeMessages(MSG_TIMEOUT);
clearAgentState();
+ addBackupTrace("operation complete");
ParcelFileDescriptor backupData = null;
mStatus = BackupConstants.TRANSPORT_OK;
@@ -2105,6 +2177,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (mStatus == BackupConstants.TRANSPORT_OK) {
backupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
+ addBackupTrace("sending data to transport");
mStatus = mTransport.performBackup(mCurrentPackage, backupData);
}
@@ -2113,11 +2186,15 @@ class BackupManagerService extends IBackupManager.Stub {
// hold off on finishBackup() until the end, which implies holding off on
// renaming *all* the output state files (see below) until that happens.
+ addBackupTrace("data delivered: " + mStatus);
if (mStatus == BackupConstants.TRANSPORT_OK) {
+ addBackupTrace("finishing op on transport");
mStatus = mTransport.finishBackup();
+ addBackupTrace("finished: " + mStatus);
}
} else {
if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
+ addBackupTrace("no data to send");
}
// After successful transport, delete the now-stale data
@@ -2165,12 +2242,14 @@ class BackupManagerService extends IBackupManager.Stub {
Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
"timeout");
+ addBackupTrace("timeout of " + mCurrentPackage.packageName);
agentErrorCleanup();
dataChangedImpl(mCurrentPackage.packageName);
}
void revertAndEndBackup() {
if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
+ addBackupTrace("transport error; reverting");
for (BackupRequest request : mOriginalQueue) {
dataChangedImpl(request.packageName);
}
@@ -2199,6 +2278,7 @@ class BackupManagerService extends IBackupManager.Stub {
// If this was a pseudopackage there's no associated Activity Manager state
if (mCurrentPackage.applicationInfo != null) {
+ addBackupTrace("unbinding " + mCurrentPackage.packageName);
try { // unbind even on timeout, just in case
mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
} catch (RemoteException e) {}
@@ -2206,6 +2286,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
void restartBackupAlarm() {
+ addBackupTrace("setting backup trigger");
synchronized (mQueueLock) {
try {
startBackupAlarmsLocked(mTransport.requestBackupTime());
@@ -2216,6 +2297,7 @@ class BackupManagerService extends IBackupManager.Stub {
void executeNextState(BackupState nextState) {
if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
+ this + " nextState=" + nextState);
+ addBackupTrace("executeNextState => " + nextState);
mCurrentState = nextState;
Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
mBackupHandler.sendMessage(msg);
@@ -2246,14 +2328,16 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor mPipe;
int mToken;
boolean mSendApk;
+ boolean mWriteManifest;
FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
- int token, boolean sendApk) throws IOException {
+ int token, boolean sendApk, boolean writeManifest) throws IOException {
mPackage = pack;
mAgent = agent;
mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
mToken = token;
mSendApk = sendApk;
+ mWriteManifest = writeManifest;
}
@Override
@@ -2262,12 +2346,14 @@ class BackupManagerService extends IBackupManager.Stub {
BackupDataOutput output = new BackupDataOutput(
mPipe.getFileDescriptor());
- if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
- writeAppManifest(mPackage, mManifestFile, mSendApk);
- FullBackup.backupToTar(mPackage.packageName, null, null,
- mFilesDir.getAbsolutePath(),
- mManifestFile.getAbsolutePath(),
- output);
+ if (mWriteManifest) {
+ if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
+ writeAppManifest(mPackage, mManifestFile, mSendApk);
+ FullBackup.backupToTar(mPackage.packageName, null, null,
+ mFilesDir.getAbsolutePath(),
+ mManifestFile.getAbsolutePath(),
+ output);
+ }
if (mSendApk) {
writeApkToBackup(mPackage, output);
@@ -2354,10 +2440,13 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
- // Cull any packages that have indicated that backups are not permitted.
+ // Cull any packages that have indicated that backups are not permitted, as well
+ // as any explicit mention of the 'special' shared-storage agent package (we
+ // handle that one at the end).
for (int i = 0; i < packagesToBackup.size(); ) {
PackageInfo pkg = packagesToBackup.get(i);
- if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0
+ || pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) {
packagesToBackup.remove(i);
} else {
i++;
@@ -2437,6 +2526,16 @@ class BackupManagerService extends IBackupManager.Stub {
return;
}
+ // Shared storage if requested
+ if (mIncludeShared) {
+ try {
+ pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
+ packagesToBackup.add(pkg);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Unable to find shared-storage backup handler");
+ }
+ }
+
// Now back up the app data via the agent mechanism
int N = packagesToBackup.size();
for (int i = 0; i < N; i++) {
@@ -2444,11 +2543,6 @@ class BackupManagerService extends IBackupManager.Stub {
backupOnePackage(pkg, out);
}
- // Shared storage if requested
- if (mIncludeShared) {
- backupSharedStorage();
- }
-
// Done!
finalizeBackup(out);
} catch (RemoteException e) {
@@ -2554,19 +2648,21 @@ class BackupManagerService extends IBackupManager.Stub {
if (agent != null) {
ParcelFileDescriptor[] pipes = null;
try {
- pipes = ParcelFileDescriptor.createPipe();
+ pipes = ParcelFileDescriptor.createPipe();
ApplicationInfo app = pkg.applicationInfo;
+ final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
final boolean sendApk = mIncludeApks
+ && !isSharedStorage
&& ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
&& ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
(app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
- sendOnBackupPackage(pkg.packageName);
+ sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
final int token = generateToken();
FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
- token, sendApk);
+ token, sendApk, !isSharedStorage);
pipes[1].close(); // the runner has dup'd it
pipes[1] = null;
Thread t = new Thread(runner);
@@ -2641,33 +2737,6 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
- private void backupSharedStorage() throws RemoteException {
- PackageInfo pkg = null;
- try {
- pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
- IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
- IApplicationThread.BACKUP_MODE_FULL);
- if (agent != null) {
- sendOnBackupPackage("Shared storage");
-
- final int token = generateToken();
- prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL, null);
- agent.doFullBackup(mOutputFile, token, mBackupManagerBinder);
- if (!waitUntilOperationComplete(token)) {
- Slog.e(TAG, "Full backup failed on shared storage");
- } else {
- if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
- }
- } else {
- Slog.e(TAG, "Could not bind to shared storage backup agent");
- }
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Shared storage backup package not found");
- } finally {
- tearDown(pkg);
- }
- }
-
private void finalizeBackup(OutputStream out) {
try {
// A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes.
@@ -2893,7 +2962,7 @@ class BackupManagerService extends IBackupManager.Stub {
// Are we able to restore shared-storage data?
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
+ mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
}
FileInputStream rawInStream = null;
@@ -3771,7 +3840,7 @@ class BackupManagerService extends IBackupManager.Stub {
info.path, 0, FullBackup.SHARED_PREFIX.length())) {
// File in shared storage. !!! TODO: implement this.
info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
- info.packageName = "com.android.sharedstoragebackup";
+ info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
info.domain = FullBackup.SHARED_STORAGE_TOKEN;
if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
} else if (FullBackup.APPS_PREFIX.regionMatches(0,
@@ -4715,6 +4784,8 @@ class BackupManagerService extends IBackupManager.Stub {
// one already there, then overwrite it, but no harm done.
BackupRequest req = new BackupRequest(packageName);
if (mPendingBackups.put(app.packageName, req) == null) {
+ if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
+
// Journal this request in case of crash. The put()
// operation returned null when this package was not already
// in the set; we want to avoid touching the disk redundantly.
@@ -5736,6 +5807,17 @@ class BackupManagerService extends IBackupManager.Stub {
pw.println(" " + s);
}
+ if (DEBUG_BACKUP_TRACE) {
+ synchronized (mBackupTrace) {
+ if (!mBackupTrace.isEmpty()) {
+ pw.println("Most recent backup trace:");
+ for (String s : mBackupTrace) {
+ pw.println(" " + s);
+ }
+ }
+ }
+ }
+
int N = mBackupParticipants.size();
pw.println("Participants:");
for (int i=0; i<N; i++) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 97fb0b0..a372fb8 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -992,11 +992,15 @@ private NetworkStateTracker makeWimaxStateTracker() {
NetworkInfo ni = network.getNetworkInfo();
if (ni.isAvailable() == false) {
- if (DBG) log("special network not available");
if (!TextUtils.equals(feature,Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
+ if (DBG) log("special network not available ni=" + ni.getTypeName());
return Phone.APN_TYPE_NOT_AVAILABLE;
} else {
// else make the attempt anyway - probably giving REQUEST_STARTED below
+ if (DBG) {
+ log("special network not available, but try anyway ni=" +
+ ni.getTypeName());
+ }
}
}
@@ -1031,9 +1035,14 @@ private NetworkStateTracker makeWimaxStateTracker() {
if ((ni.isConnectedOrConnecting() == true) &&
!network.isTeardownRequested()) {
if (ni.isConnected() == true) {
- // add the pid-specific dns
- handleDnsConfigurationChange(usedNetworkType);
- if (VDBG) log("special network already active");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // add the pid-specific dns
+ handleDnsConfigurationChange(usedNetworkType);
+ if (VDBG) log("special network already active");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
return Phone.APN_ALREADY_ACTIVE;
}
if (VDBG) log("special network already connecting");
@@ -1221,6 +1230,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
+ if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
return false;
}
NetworkStateTracker tracker = mNetTrackers[networkType];
@@ -1233,11 +1243,16 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
return false;
}
+ final long token = Binder.clearCallingIdentity();
try {
InetAddress addr = InetAddress.getByAddress(hostAddress);
LinkProperties lp = tracker.getLinkProperties();
return addRouteToAddress(lp, addr);
- } catch (UnknownHostException e) {}
+ } catch (UnknownHostException e) {
+ if (DBG) log("requestRouteToHostAddress got " + e.toString());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
return false;
}
@@ -1277,7 +1292,10 @@ private NetworkStateTracker makeWimaxStateTracker() {
private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
boolean doAdd, boolean toDefaultTable) {
- if ((ifaceName == null) || (lp == null) || (r == null)) return false;
+ if ((ifaceName == null) || (lp == null) || (r == null)) {
+ if (DBG) log("modifyRoute got unexpected null: " + ifaceName + ", " + lp + ", " + r);
+ return false;
+ }
if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
loge("Error modifying route - too much recursion");
@@ -1309,7 +1327,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
} catch (Exception e) {
// never crash - catch them all
- if (VDBG) loge("Exception trying to add a route: " + e);
+ if (DBG) loge("Exception trying to add a route: " + e);
return false;
}
} else {
@@ -1323,7 +1341,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
mNetd.removeRoute(ifaceName, r);
} catch (Exception e) {
// never crash - catch them all
- if (VDBG) loge("Exception trying to remove a route: " + e);
+ if (DBG) loge("Exception trying to remove a route: " + e);
return false;
}
} else {
@@ -1335,7 +1353,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
mNetd.removeSecondaryRoute(ifaceName, r);
} catch (Exception e) {
// never crash - catch them all
- if (VDBG) loge("Exception trying to remove a route: " + e);
+ if (DBG) loge("Exception trying to remove a route: " + e);
return false;
}
}
@@ -2004,7 +2022,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
mNetd.removeRoute(ifaceName, r);
} catch (Exception e) {
// never crash - catch them all
- if (VDBG) loge("Exception trying to remove a route: " + e);
+ if (DBG) loge("Exception trying to remove a route: " + e);
}
}
}
@@ -2218,7 +2236,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses));
mNetd.setDefaultInterfaceForDns(iface);
} catch (Exception e) {
- if (VDBG) loge("exception setting default dns interface: " + e);
+ if (DBG) loge("exception setting default dns interface: " + e);
}
}
if (!domains.equals(SystemProperties.get("net.dns.search"))) {
@@ -2248,7 +2266,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
mNetd.setDnsServersForInterface(p.getInterfaceName(),
NetworkUtils.makeStrings(dnses));
} catch (Exception e) {
- if (VDBG) loge("exception setting dns servers: " + e);
+ if (DBG) loge("exception setting dns servers: " + e);
}
// set per-pid dns for attached secondary nets
List pids = mNetRequestersPids[netType];
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyMixer.java
index 788a2f5..b63a70e 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyMixer.java
@@ -47,8 +47,8 @@ import android.util.Slog;
* <p>TODO: Investigate attempting to write entropy data at shutdown time
* instead of periodically.
*/
-public class EntropyService extends Binder {
- private static final String TAG = "EntropyService";
+public class EntropyMixer extends Binder {
+ private static final String TAG = "EntropyMixer";
private static final int ENTROPY_WHAT = 1;
private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
private static final long START_TIME = System.currentTimeMillis();
@@ -72,12 +72,12 @@ public class EntropyService extends Binder {
}
};
- public EntropyService() {
+ public EntropyMixer() {
this(getSystemDir() + "/entropy.dat", "/dev/urandom");
}
/** Test only interface, not for public use */
- public EntropyService(String entropyFile, String randomDevice) {
+ public EntropyMixer(String entropyFile, String randomDevice) {
if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 4dad209..0bcec2e 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -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),(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)
+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)
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index f5c4ed4..aba1bc6 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -142,6 +142,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
+ private static final String TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
+ "EnabledWhenDefaultIsNotAsciiCapable";
+ private static final String TAG_ASCII_CAPABLE = "AsciiCapable";
final Context mContext;
final Resources mRes;
@@ -2163,6 +2166,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
+ final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtype();
+ if (currentSubtype != null) {
+ final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
+ lastInputMethodSubtypeId =
+ getSubtypeIdFromHashCode(currentImi, currentSubtype.hashCode());
+ }
+ }
+
final int N = imList.size();
mIms = new InputMethodInfo[N];
mSubtypeIds = new int[N];
@@ -2472,7 +2484,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap =
new HashMap<String, InputMethodSubtype>();
final int N = subtypes.size();
- boolean containsKeyboardSubtype = false;
for (int i = 0; i < N; ++i) {
// scan overriding implicitly enabled subtypes.
InputMethodSubtype subtype = subtypes.get(i);
@@ -2506,15 +2517,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!systemLocale.equals(locale)) continue;
}
applicableModeAndSubtypesMap.put(mode, subtype);
- if (!containsKeyboardSubtype
- && SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
- containsKeyboardSubtype = true;
- }
}
}
+ final InputMethodSubtype keyboardSubtype
+ = applicableModeAndSubtypesMap.get(SUBTYPE_MODE_KEYBOARD);
final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
applicableModeAndSubtypesMap.values());
- if (!containsKeyboardSubtype) {
+ if (keyboardSubtype != null && !keyboardSubtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) {
+ for (int i = 0; i < N; ++i) {
+ final InputMethodSubtype subtype = subtypes.get(i);
+ final String mode = subtype.getMode();
+ if (SUBTYPE_MODE_KEYBOARD.equals(mode) && subtype.containsExtraValueKey(
+ TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)) {
+ applicableSubtypes.add(subtype);
+ }
+ }
+ }
+ if (keyboardSubtype == null) {
InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true);
if (lastResortKeyboardSubtype != null) {
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 56afe7f..8cb9d99b 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -1962,8 +1962,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
} else {
mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
}
- NetworkInfo info =
- (NetworkInfo)intent.getExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+
+ final ConnectivityManager connManager = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo info = connManager.getActiveNetworkInfo();
// Notify location providers of current network state
synchronized (mLock) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 5425813..366160b 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -20,6 +20,7 @@ import com.android.internal.app.IMediaContainerService;
import com.android.internal.util.XmlUtils;
import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageManagerService;
+import com.android.server.NativeDaemonConnector.Command;
import android.Manifest;
import android.content.BroadcastReceiver;
@@ -564,8 +565,7 @@ class MountService extends IMountService.Stub
}
try {
- mConnector.doCommand(String.format(
- "volume %sshare %s %s", (enable ? "" : "un"), path, method));
+ mConnector.execute("volume", enable ? "share" : "unshare", path, method);
} catch (NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to share/unshare", e);
}
@@ -633,8 +633,9 @@ class MountService extends IMountService.Stub
* Determine media state and UMS detection status
*/
try {
- String[] vols = mConnector.doListCommand(
- "volume list", VoldResponseCode.VolumeListResult);
+ final String[] vols = NativeDaemonEvent.filterMessageList(
+ mConnector.executeForList("volume", "list"),
+ VoldResponseCode.VolumeListResult);
for (String volstr : vols) {
String[] tok = volstr.split(" ");
// FMT: <label> <mountpoint> <state>
@@ -839,7 +840,7 @@ class MountService extends IMountService.Stub
if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
try {
- mConnector.doCommand(String.format("volume mount %s", path));
+ mConnector.execute("volume", "mount", path);
} catch (NativeDaemonConnectorException e) {
/*
* Mount failed for some reason
@@ -909,10 +910,13 @@ class MountService extends IMountService.Stub
// Redundant probably. But no harm in updating state again.
mPms.updateExternalMediaStatus(false, false);
try {
- String arg = removeEncryption
- ? " force_and_revert"
- : (force ? " force" : "");
- mConnector.doCommand(String.format("volume unmount %s%s", path, arg));
+ final Command cmd = new Command("volume", "unmount", path);
+ if (removeEncryption) {
+ cmd.appendArg("force_and_revert");
+ } else if (force) {
+ cmd.appendArg("force");
+ }
+ mConnector.execute(cmd);
// We unmounted the volume. None of the asec containers are available now.
synchronized (mAsecMountSet) {
mAsecMountSet.clear();
@@ -934,8 +938,7 @@ class MountService extends IMountService.Stub
private int doFormatVolume(String path) {
try {
- String cmd = String.format("volume format %s", path);
- mConnector.doCommand(cmd);
+ mConnector.execute("volume", "format", path);
return StorageResultCode.OperationSucceeded;
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
@@ -950,39 +953,19 @@ class MountService extends IMountService.Stub
}
private boolean doGetVolumeShared(String path, String method) {
- String cmd = String.format("volume shared %s %s", path, method);
- ArrayList<String> rsp;
-
+ final NativeDaemonEvent event;
try {
- rsp = mConnector.doCommand(cmd);
+ event = mConnector.execute("volume", "shared", path, method);
} catch (NativeDaemonConnectorException ex) {
Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
return false;
}
- for (String line : rsp) {
- String[] tok = line.split(" ");
- if (tok.length < 3) {
- Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
- return false;
- }
-
- int code;
- try {
- code = Integer.parseInt(tok[0]);
- } catch (NumberFormatException nfe) {
- Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
- return false;
- }
- if (code == VoldResponseCode.ShareEnabledResult) {
- return "enabled".equals(tok[2]);
- } else {
- Slog.e(TAG, String.format("Unexpected response code %d", code));
- return false;
- }
+ if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
+ return event.getMessage().endsWith("enabled");
+ } else {
+ return false;
}
- Slog.e(TAG, "Got an empty response");
- return false;
}
private void notifyShareAvailabilityChange(final boolean avail) {
@@ -1186,7 +1169,7 @@ class MountService extends IMountService.Stub
* amount of containers we'd ever expect to have. This keeps an
* "asec list" from blocking a thread repeatedly.
*/
- mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG);
+ mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);
mReady = false;
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
@@ -1410,13 +1393,14 @@ class MountService extends IMountService.Stub
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
try {
- String[] r = mConnector.doListCommand(
- String.format("storage users %s", path),
- VoldResponseCode.StorageUsersListResult);
+ final String[] r = NativeDaemonEvent.filterMessageList(
+ mConnector.executeForList("storage", "users", path),
+ VoldResponseCode.StorageUsersListResult);
+
// FMT: <pid> <process name>
int[] data = new int[r.length];
for (int i = 0; i < r.length; i++) {
- String []tok = r[i].split(" ");
+ String[] tok = r[i].split(" ");
try {
data[i] = Integer.parseInt(tok[0]);
} catch (NumberFormatException nfe) {
@@ -1443,7 +1427,8 @@ class MountService extends IMountService.Stub
warnOnNotMounted();
try {
- return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
+ return NativeDaemonEvent.filterMessageList(
+ mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);
} catch (NativeDaemonConnectorException e) {
return new String[0];
}
@@ -1456,9 +1441,8 @@ class MountService extends IMountService.Stub
warnOnNotMounted();
int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
try {
- mConnector.doCommand(cmd);
+ mConnector.execute("asec", "create", id, sizeMb, fstype, key, ownerUid);
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
@@ -1477,7 +1461,7 @@ class MountService extends IMountService.Stub
int rc = StorageResultCode.OperationSucceeded;
try {
- mConnector.doCommand(String.format("asec finalize %s", id));
+ mConnector.execute("asec", "finalize", id);
/*
* Finalization does a remount, so no need
* to update mAsecMountSet
@@ -1503,7 +1487,11 @@ class MountService extends IMountService.Stub
int rc = StorageResultCode.OperationSucceeded;
try {
- mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
+ final Command cmd = new Command("asec", "destroy", id);
+ if (force) {
+ cmd.appendArg("force");
+ }
+ mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageBusy) {
@@ -1536,9 +1524,8 @@ class MountService extends IMountService.Stub
}
int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
try {
- mConnector.doCommand(cmd);
+ mConnector.execute("asec", "mount", id, key, ownerUid);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code != VoldResponseCode.OpFailedStorageBusy) {
@@ -1574,9 +1561,12 @@ class MountService extends IMountService.Stub
Runtime.getRuntime().gc();
int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
try {
- mConnector.doCommand(cmd);
+ final Command cmd = new Command("asec", "unmount", id);
+ if (force) {
+ cmd.appendArg("force");
+ }
+ mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageBusy) {
@@ -1620,9 +1610,8 @@ class MountService extends IMountService.Stub
}
int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("asec rename %s %s", oldId, newId);
try {
- mConnector.doCommand(cmd);
+ mConnector.execute("asec", "rename", oldId, newId);
} catch (NativeDaemonConnectorException e) {
rc = StorageResultCode.OperationFailedInternalError;
}
@@ -1635,14 +1624,11 @@ class MountService extends IMountService.Stub
waitForReady();
warnOnNotMounted();
+ final NativeDaemonEvent event;
try {
- ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id));
- String []tok = rsp.get(0).split(" ");
- int code = Integer.parseInt(tok[0]);
- if (code != VoldResponseCode.AsecPathResult) {
- throw new IllegalStateException(String.format("Unexpected response code %d", code));
- }
- return tok[1];
+ event = mConnector.execute("asec", "path", id);
+ event.checkCode(VoldResponseCode.AsecPathResult);
+ return event.getMessage();
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) {
@@ -1659,14 +1645,11 @@ class MountService extends IMountService.Stub
waitForReady();
warnOnNotMounted();
+ final NativeDaemonEvent event;
try {
- ArrayList<String> rsp = mConnector.doCommand(String.format("asec fspath %s", id));
- String []tok = rsp.get(0).split(" ");
- int code = Integer.parseInt(tok[0]);
- if (code != VoldResponseCode.AsecPathResult) {
- throw new IllegalStateException(String.format("Unexpected response code %d", code));
- }
- return tok[1];
+ event = mConnector.execute("asec", "fspath", id);
+ event.checkCode(VoldResponseCode.AsecPathResult);
+ return event.getMessage();
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) {
@@ -1709,14 +1692,11 @@ class MountService extends IMountService.Stub
waitForReady();
warnOnNotMounted();
+ final NativeDaemonEvent event;
try {
- ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
- String []tok = rsp.get(0).split(" ");
- int code = Integer.parseInt(tok[0]);
- if (code != VoldResponseCode.AsecPathResult) {
- throw new IllegalStateException(String.format("Unexpected response code %d", code));
- }
- return tok[1];
+ event = mConnector.execute("obb", "path", filename);
+ event.checkCode(VoldResponseCode.AsecPathResult);
+ return event.getMessage();
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) {
@@ -1778,18 +1758,10 @@ class MountService extends IMountService.Stub
waitForReady();
+ final NativeDaemonEvent event;
try {
- ArrayList<String> rsp = mConnector.doCommand("cryptfs cryptocomplete");
- String[] tokens = rsp.get(0).split(" ");
-
- if (tokens == null || tokens.length != 2) {
- // Unexpected.
- Slog.w(TAG, "Unexpected result from cryptfs cryptocomplete");
- return ENCRYPTION_STATE_ERROR_UNKNOWN;
- }
-
- return Integer.parseInt(tokens[1]);
-
+ event = mConnector.execute("cryptfs", "cryptocomplete");
+ return Integer.parseInt(event.getMessage());
} catch (NumberFormatException e) {
// Bad result - unexpected.
Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete");
@@ -1816,22 +1788,21 @@ class MountService extends IMountService.Stub
Slog.i(TAG, "decrypting storage...");
}
+ final NativeDaemonEvent event;
try {
- ArrayList<String> rsp = mConnector.doCommand("cryptfs checkpw " + password);
- String[] tokens = rsp.get(0).split(" ");
-
- if (tokens == null || tokens.length != 2) {
- return -1;
- }
-
- int code = Integer.parseInt(tokens[1]);
+ event = mConnector.execute("cryptfs", "checkpw", password);
+ final int code = Integer.parseInt(event.getMessage());
if (code == 0) {
// Decrypt was successful. Post a delayed message before restarting in order
// to let the UI to clear itself
mHandler.postDelayed(new Runnable() {
public void run() {
- mConnector.doCommand(String.format("cryptfs restart"));
+ try {
+ mConnector.execute("cryptfs", "restart");
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "problem executing in background", e);
+ }
}
}, 1000); // 1 second
}
@@ -1858,7 +1829,7 @@ class MountService extends IMountService.Stub
}
try {
- mConnector.doCommand(String.format("cryptfs enablecrypto inplace %s", password));
+ mConnector.execute("cryptfs", "enablecrypto", "inplace", password);
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
@@ -1881,16 +1852,10 @@ class MountService extends IMountService.Stub
Slog.i(TAG, "changing encryption password...");
}
+ final NativeDaemonEvent event;
try {
- ArrayList<String> response = mConnector.doCommand("cryptfs changepw " + password);
-
- String[] tokens = response.get(0).split(" ");
-
- if (tokens == null || tokens.length != 2) {
- return -1;
- }
-
- return Integer.parseInt(tokens[1]);
+ event = mConnector.execute("cryptfs", "changepw", password);
+ return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
@@ -1920,24 +1885,11 @@ class MountService extends IMountService.Stub
Slog.i(TAG, "validating encryption password...");
}
+ final NativeDaemonEvent event;
try {
- ArrayList<String> response = mConnector.doCommand("cryptfs verifypw " + password);
- String[] tokens = response.get(0).split(" ");
-
- if (tokens == null || tokens.length != 2) {
- String msg = "Unexpected result from cryptfs verifypw: {";
- if (tokens == null) msg += "null";
- else for (int i = 0; i < tokens.length; i++) {
- if (i != 0) msg += ',';
- msg += tokens[i];
- }
- msg += '}';
- Slog.e(TAG, msg);
- return -1;
- }
-
- Slog.i(TAG, "cryptfs verifypw => " + tokens[1]);
- return Integer.parseInt(tokens[1]);
+ event = mConnector.execute("cryptfs", "verifypw", password);
+ Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
+ return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
@@ -2296,10 +2248,9 @@ class MountService extends IMountService.Stub
}
int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey,
- mObbState.callerUid);
try {
- mConnector.doCommand(cmd);
+ mConnector.execute(
+ "obb", "mount", mObbState.filename, hashedKey, mObbState.callerUid);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code != VoldResponseCode.OpFailedStorageBusy) {
@@ -2380,10 +2331,12 @@ class MountService extends IMountService.Stub
mObbState.filename = obbInfo.filename;
int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb unmount %s%s", mObbState.filename,
- (mForceUnmount ? " force" : ""));
try {
- mConnector.doCommand(cmd);
+ final Command cmd = new Command("obb", "unmount", mObbState.filename);
+ if (mForceUnmount) {
+ cmd.appendArg("force");
+ }
+ mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageBusy) {
@@ -2476,6 +2429,10 @@ class MountService extends IMountService.Stub
pw.println(v.toString());
}
}
+
+ pw.println();
+ pw.println(" mConnection:");
+ mConnector.dump(fd, pw, args);
}
/** {@inheritDoc} */
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 28013bd..308661f 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -22,59 +22,51 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
+import android.util.LocalLog;
import android.util.Slog;
+import com.google.android.collect.Lists;
+
+import java.nio.charset.Charsets;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
- * Generic connector class for interfacing with a native
- * daemon which uses the libsysutils FrameworkListener
- * protocol.
+ * Generic connector class for interfacing with a native daemon which uses the
+ * {@code libsysutils} FrameworkListener protocol.
*/
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
- private static final boolean LOCAL_LOGD = false;
+ private static final boolean LOGD = false;
- private BlockingQueue<String> mResponseQueue;
- private OutputStream mOutputStream;
- private String TAG = "NativeDaemonConnector";
- private String mSocket;
- private INativeDaemonConnectorCallbacks mCallbacks;
- private Handler mCallbackHandler;
+ private final String TAG;
- /** Lock held whenever communicating with native daemon. */
- private Object mDaemonLock = new Object();
+ private String mSocket;
+ private OutputStream mOutputStream;
+ private LocalLog mLocalLog;
- private final int BUFFER_SIZE = 4096;
+ private final BlockingQueue<NativeDaemonEvent> mResponseQueue;
- class ResponseCode {
- public static final int ActionInitiated = 100;
-
- public static final int CommandOkay = 200;
-
- // The range of 400 -> 599 is reserved for cmd failures
- public static final int OperationFailed = 400;
- public static final int CommandSyntaxError = 500;
- public static final int CommandParameterError = 501;
+ private INativeDaemonConnectorCallbacks mCallbacks;
+ private Handler mCallbackHandler;
- public static final int UnsolicitedInformational = 600;
+ /** Lock held whenever communicating with native daemon. */
+ private final Object mDaemonLock = new Object();
- //
- public static final int FailedRangeStart = 400;
- public static final int FailedRangeEnd = 599;
- }
+ private final int BUFFER_SIZE = 4096;
- NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks,
- String socket, int responseQueueSize, String logTag) {
+ NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
+ int responseQueueSize, String logTag, int maxLogSize) {
mCallbacks = callbacks;
- if (logTag != null)
- TAG = logTag;
mSocket = socket;
- mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize);
+ mResponseQueue = new LinkedBlockingQueue<NativeDaemonEvent>(responseQueueSize);
+ TAG = logTag != null ? logTag : "NativeDaemonConnector";
+ mLocalLog = new LocalLog(maxLogSize);
}
@Override
@@ -136,29 +128,35 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
- String event = new String(buffer, start, i - start);
- if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event));
+ final String rawEvent = new String(
+ buffer, start, i - start, Charsets.UTF_8);
+ log("RCV <- {" + rawEvent + "}");
- String[] tokens = event.split(" ", 2);
try {
- int code = Integer.parseInt(tokens[0]);
-
- if (code >= ResponseCode.UnsolicitedInformational) {
- mCallbackHandler.sendMessage(
- mCallbackHandler.obtainMessage(code, event));
+ final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
+ rawEvent);
+ if (event.isClassUnsolicited()) {
+ // TODO: migrate to sending NativeDaemonEvent instances
+ mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
+ event.getCode(), event.getRawEvent()));
} else {
try {
mResponseQueue.put(event);
} catch (InterruptedException ex) {
- Slog.e(TAG, "Failed to put response onto queue", ex);
+ Slog.e(TAG, "Failed to put response onto queue: " + ex);
}
}
- } catch (NumberFormatException nfe) {
- Slog.w(TAG, String.format("Bad msg (%s)", event));
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Problem parsing message: " + rawEvent, e);
}
+
start = i + 1;
}
}
+ if (start == 0) {
+ final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
+ log("RCV incomplete <- {" + rawEvent + "}");
+ }
// We should end at the amount we read. If not, compact then
// buffer and read again.
@@ -195,137 +193,265 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
}
}
- private void sendCommandLocked(String command) throws NativeDaemonConnectorException {
- sendCommandLocked(command, null);
- }
-
/**
- * Sends a command to the daemon with a single argument
+ * Send command to daemon, escaping arguments as needed.
*
- * @param command The command to send to the daemon
- * @param argument The argument to send with the command (or null)
+ * @return the final command issued.
*/
- private void sendCommandLocked(String command, String argument)
+ private String sendCommandLocked(String cmd, Object... args)
throws NativeDaemonConnectorException {
- if (command != null && command.indexOf('\0') >= 0) {
- throw new IllegalArgumentException("unexpected command: " + command);
+ // TODO: eventually enforce that cmd doesn't contain arguments
+ if (cmd.indexOf('\0') >= 0) {
+ throw new IllegalArgumentException("unexpected command: " + cmd);
}
- if (argument != null && argument.indexOf('\0') >= 0) {
- throw new IllegalArgumentException("unexpected argument: " + argument);
+
+ final StringBuilder builder = new StringBuilder(cmd);
+ for (Object arg : args) {
+ final String argString = String.valueOf(arg);
+ if (argString.indexOf('\0') >= 0) {
+ throw new IllegalArgumentException("unexpected argument: " + arg);
+ }
+
+ builder.append(' ');
+ appendEscaped(builder, argString);
}
- if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument));
+ final String unterminated = builder.toString();
+ log("SND -> {" + unterminated + "}");
+
+ builder.append('\0');
+
if (mOutputStream == null) {
- Slog.e(TAG, "No connection to daemon", new IllegalStateException());
- throw new NativeDaemonConnectorException("No output stream!");
+ throw new NativeDaemonConnectorException("missing output stream");
} else {
- StringBuilder builder = new StringBuilder(command);
- if (argument != null) {
- builder.append(argument);
- }
- builder.append('\0');
-
try {
- mOutputStream.write(builder.toString().getBytes());
- } catch (IOException ex) {
- Slog.e(TAG, "IOException in sendCommand", ex);
+ mOutputStream.write(builder.toString().getBytes(Charsets.UTF_8));
+ } catch (IOException e) {
+ throw new NativeDaemonConnectorException("problem sending command", e);
}
}
+
+ return unterminated;
+ }
+
+ /**
+ * Issue the given command to the native daemon and return a single expected
+ * response.
+ *
+ * @throws NativeDaemonConnectorException when problem communicating with
+ * native daemon, or if the response matches
+ * {@link NativeDaemonEvent#isClassClientError()} or
+ * {@link NativeDaemonEvent#isClassServerError()}.
+ */
+ public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException {
+ return execute(cmd.mCmd, cmd.mArguments.toArray());
+ }
+
+ /**
+ * Issue the given command to the native daemon and return a single expected
+ * response.
+ *
+ * @throws NativeDaemonConnectorException when problem communicating with
+ * native daemon, or if the response matches
+ * {@link NativeDaemonEvent#isClassClientError()} or
+ * {@link NativeDaemonEvent#isClassServerError()}.
+ */
+ public NativeDaemonEvent execute(String cmd, Object... args)
+ throws NativeDaemonConnectorException {
+ final NativeDaemonEvent[] events = executeForList(cmd, args);
+ if (events.length != 1) {
+ throw new NativeDaemonConnectorException(
+ "Expected exactly one response, but received " + events.length);
+ }
+ return events[0];
}
/**
- * Issue a command to the native daemon and return the responses
+ * Issue the given command to the native daemon and return any
+ * {@link NativeDaemonEvent#isClassContinue()} responses, including the
+ * final terminal response.
+ *
+ * @throws NativeDaemonConnectorException when problem communicating with
+ * native daemon, or if the response matches
+ * {@link NativeDaemonEvent#isClassClientError()} or
+ * {@link NativeDaemonEvent#isClassServerError()}.
*/
- public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException {
+ public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException {
+ return executeForList(cmd.mCmd, cmd.mArguments.toArray());
+ }
+
+ /**
+ * Issue the given command to the native daemon and return any
+ * {@link NativeDaemonEvent#isClassContinue()} responses, including the
+ * final terminal response.
+ *
+ * @throws NativeDaemonConnectorException when problem communicating with
+ * native daemon, or if the response matches
+ * {@link NativeDaemonEvent#isClassClientError()} or
+ * {@link NativeDaemonEvent#isClassServerError()}.
+ */
+ public NativeDaemonEvent[] executeForList(String cmd, Object... args)
+ throws NativeDaemonConnectorException {
synchronized (mDaemonLock) {
- return doCommandLocked(cmd);
+ return executeLocked(cmd, args);
}
}
- private ArrayList<String> doCommandLocked(String cmd) throws NativeDaemonConnectorException {
- mResponseQueue.clear();
- sendCommandLocked(cmd);
-
- ArrayList<String> response = new ArrayList<String>();
- boolean complete = false;
- int code = -1;
+ private NativeDaemonEvent[] executeLocked(String cmd, Object... args)
+ throws NativeDaemonConnectorException {
+ final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
- while (!complete) {
+ while (mResponseQueue.size() > 0) {
try {
- // TODO - this should not block forever
- String line = mResponseQueue.take();
- if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line));
- String[] tokens = line.split(" ");
- try {
- code = Integer.parseInt(tokens[0]);
- } catch (NumberFormatException nfe) {
- throw new NativeDaemonConnectorException(
- String.format("Invalid response from daemon (%s)", line));
- }
+ log("ignoring {" + mResponseQueue.take() + "}");
+ } catch (Exception e) {}
+ }
- if ((code >= 200) && (code < 600)) {
- complete = true;
- }
- response.add(line);
- } catch (InterruptedException ex) {
- Slog.e(TAG, "Failed to process response", ex);
+ final String sentCommand = sendCommandLocked(cmd, args);
+
+ NativeDaemonEvent event = null;
+ do {
+ try {
+ event = mResponseQueue.take();
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "interrupted waiting for event line");
+ continue;
}
+ events.add(event);
+ } while (event.isClassContinue());
+
+ if (event.isClassClientError()) {
+ throw new NativeDaemonArgumentException(sentCommand, event);
+ }
+ if (event.isClassServerError()) {
+ throw new NativeDaemonFailureException(sentCommand, event);
}
- if (code >= ResponseCode.FailedRangeStart &&
- code <= ResponseCode.FailedRangeEnd) {
- /*
- * Note: The format of the last response in this case is
- * "NNN <errmsg>"
- */
- throw new NativeDaemonConnectorException(
- code, cmd, response.get(response.size()-1).substring(4));
+ return events.toArray(new NativeDaemonEvent[events.size()]);
+ }
+
+ /**
+ * Issue a command to the native daemon and return the raw responses.
+ *
+ * @deprecated callers should move to {@link #execute(String, Object...)}
+ * which returns parsed {@link NativeDaemonEvent}.
+ */
+ @Deprecated
+ public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException {
+ final ArrayList<String> rawEvents = Lists.newArrayList();
+ final NativeDaemonEvent[] events = executeForList(cmd);
+ for (NativeDaemonEvent event : events) {
+ rawEvents.add(event.getRawEvent());
}
- return response;
+ return rawEvents;
}
/**
- * Issues a list command and returns the cooked list
+ * Issues a list command and returns the cooked list of all
+ * {@link NativeDaemonEvent#getMessage()} which match requested code.
*/
- public String[] doListCommand(String cmd, int expectedResponseCode)
+ @Deprecated
+ public String[] doListCommand(String cmd, int expectedCode)
throws NativeDaemonConnectorException {
+ final ArrayList<String> list = Lists.newArrayList();
+
+ final NativeDaemonEvent[] events = executeForList(cmd);
+ for (int i = 0; i < events.length - 1; i++) {
+ final NativeDaemonEvent event = events[i];
+ final int code = event.getCode();
+ if (code == expectedCode) {
+ list.add(event.getMessage());
+ } else {
+ throw new NativeDaemonConnectorException(
+ "unexpected list response " + code + " instead of " + expectedCode);
+ }
+ }
- ArrayList<String> rsp = doCommand(cmd);
- String[] rdata = new String[rsp.size()-1];
- int idx = 0;
+ final NativeDaemonEvent finalEvent = events[events.length - 1];
+ if (!finalEvent.isClassOk()) {
+ throw new NativeDaemonConnectorException("unexpected final event: " + finalEvent);
+ }
- for (int i = 0; i < rsp.size(); i++) {
- String line = rsp.get(i);
- try {
- String[] tok = line.split(" ");
- int code = Integer.parseInt(tok[0]);
- if (code == expectedResponseCode) {
- rdata[idx++] = line.substring(tok[0].length() + 1);
- } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
- if (LOCAL_LOGD) Slog.d(TAG, String.format("List terminated with {%s}", line));
- int last = rsp.size() -1;
- if (i != last) {
- Slog.w(TAG, String.format("Recv'd %d lines after end of list {%s}", (last-i), cmd));
- for (int j = i; j <= last ; j++) {
- Slog.w(TAG, String.format("ExtraData <%s>", rsp.get(i)));
- }
- }
- return rdata;
- } else {
- throw new NativeDaemonConnectorException(
- String.format("Expected list response %d, but got %d",
- expectedResponseCode, code));
- }
- } catch (NumberFormatException nfe) {
- throw new NativeDaemonConnectorException(
- String.format("Error reading code '%s'", line));
+ return list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Append the given argument to {@link StringBuilder}, escaping as needed,
+ * and surrounding with quotes when it contains spaces.
+ */
+ // @VisibleForTesting
+ static void appendEscaped(StringBuilder builder, String arg) {
+ final boolean hasSpaces = arg.indexOf(' ') >= 0;
+ if (hasSpaces) {
+ builder.append('"');
+ }
+
+ final int length = arg.length();
+ for (int i = 0; i < length; i++) {
+ final char c = arg.charAt(i);
+
+ if (c == '"') {
+ builder.append("\\\"");
+ } else if (c == '\\') {
+ builder.append("\\\\");
+ } else {
+ builder.append(c);
}
}
- throw new NativeDaemonConnectorException("Got an empty response");
+
+ if (hasSpaces) {
+ builder.append('"');
+ }
+ }
+
+ private static class NativeDaemonArgumentException extends NativeDaemonConnectorException {
+ public NativeDaemonArgumentException(String command, NativeDaemonEvent event) {
+ super(command, event);
+ }
+
+ @Override
+ public IllegalArgumentException rethrowAsParcelableException() {
+ throw new IllegalArgumentException(getMessage(), this);
+ }
+ }
+
+ private static class NativeDaemonFailureException extends NativeDaemonConnectorException {
+ public NativeDaemonFailureException(String command, NativeDaemonEvent event) {
+ super(command, event);
+ }
+ }
+
+ /**
+ * Command builder that handles argument list building.
+ */
+ public static class Command {
+ private String mCmd;
+ private ArrayList<Object> mArguments = Lists.newArrayList();
+
+ public Command(String cmd, Object... args) {
+ mCmd = cmd;
+ for (Object arg : args) {
+ appendArg(arg);
+ }
+ }
+
+ public Command appendArg(Object arg) {
+ mArguments.add(arg);
+ return this;
+ }
}
/** {@inheritDoc} */
public void monitor() {
synchronized (mDaemonLock) { }
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mLocalLog.dump(fd, pw, args);
+ }
+
+ private void log(String logstring) {
+ if (LOGD) Slog.d(TAG, logstring);
+ mLocalLog.log(logstring);
+ }
}
diff --git a/services/java/com/android/server/NativeDaemonConnectorException.java b/services/java/com/android/server/NativeDaemonConnectorException.java
index 426742b..590bbcc 100644
--- a/services/java/com/android/server/NativeDaemonConnectorException.java
+++ b/services/java/com/android/server/NativeDaemonConnectorException.java
@@ -16,33 +16,43 @@
package com.android.server;
+import android.os.Parcel;
+
/**
- * An exception that indicates there was an error with a NativeDaemonConnector operation
+ * An exception that indicates there was an error with a
+ * {@link NativeDaemonConnector} operation.
*/
-public class NativeDaemonConnectorException extends RuntimeException
-{
- private int mCode = -1;
+public class NativeDaemonConnectorException extends Exception {
private String mCmd;
+ private NativeDaemonEvent mEvent;
- public NativeDaemonConnectorException() {}
+ public NativeDaemonConnectorException(String detailMessage) {
+ super(detailMessage);
+ }
- public NativeDaemonConnectorException(String error)
- {
- super(error);
+ public NativeDaemonConnectorException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
}
- public NativeDaemonConnectorException(int code, String cmd, String error)
- {
- super(String.format("Cmd {%s} failed with code %d : {%s}", cmd, code, error));
- mCode = code;
+ public NativeDaemonConnectorException(String cmd, NativeDaemonEvent event) {
+ super("command '" + cmd + "' failed with '" + event + "'");
mCmd = cmd;
+ mEvent = event;
}
public int getCode() {
- return mCode;
+ return mEvent.getCode();
}
public String getCmd() {
return mCmd;
}
+
+ /**
+ * Rethrow as a {@link RuntimeException} subclass that is handled by
+ * {@link Parcel#writeException(Exception)}.
+ */
+ public IllegalArgumentException rethrowAsParcelableException() {
+ throw new IllegalStateException(getMessage(), this);
+ }
}
diff --git a/services/java/com/android/server/NativeDaemonEvent.java b/services/java/com/android/server/NativeDaemonEvent.java
new file mode 100644
index 0000000..62084c0
--- /dev/null
+++ b/services/java/com/android/server/NativeDaemonEvent.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
+/**
+ * Parsed event from native side of {@link NativeDaemonConnector}.
+ */
+public class NativeDaemonEvent {
+
+ // TODO: keep class ranges in sync with ResponseCode.h
+ // TODO: swap client and server error ranges to roughly mirror HTTP spec
+
+ private final int mCode;
+ private final String mMessage;
+ private final String mRawEvent;
+
+ private NativeDaemonEvent(int code, String message, String rawEvent) {
+ mCode = code;
+ mMessage = message;
+ mRawEvent = rawEvent;
+ }
+
+ public int getCode() {
+ return mCode;
+ }
+
+ public String getMessage() {
+ return mMessage;
+ }
+
+ @Deprecated
+ public String getRawEvent() {
+ return mRawEvent;
+ }
+
+ @Override
+ public String toString() {
+ return mRawEvent;
+ }
+
+ /**
+ * Test if event represents a partial response which is continued in
+ * additional subsequent events.
+ */
+ public boolean isClassContinue() {
+ return mCode >= 100 && mCode < 200;
+ }
+
+ /**
+ * Test if event represents a command success.
+ */
+ public boolean isClassOk() {
+ return mCode >= 200 && mCode < 300;
+ }
+
+ /**
+ * Test if event represents a remote native daemon error.
+ */
+ public boolean isClassServerError() {
+ return mCode >= 400 && mCode < 500;
+ }
+
+ /**
+ * Test if event represents a command syntax or argument error.
+ */
+ public boolean isClassClientError() {
+ return mCode >= 500 && mCode < 600;
+ }
+
+ /**
+ * Test if event represents an unsolicited event from native daemon.
+ */
+ public boolean isClassUnsolicited() {
+ return mCode >= 600 && mCode < 700;
+ }
+
+ /**
+ * Verify this event matches the given code.
+ *
+ * @throws IllegalStateException if {@link #getCode()} doesn't match.
+ */
+ public void checkCode(int code) {
+ if (mCode != code) {
+ throw new IllegalStateException("Expected " + code + " but was: " + this);
+ }
+ }
+
+ /**
+ * Parse the given raw event into {@link NativeDaemonEvent} instance.
+ *
+ * @throws IllegalArgumentException when line doesn't match format expected
+ * from native side.
+ */
+ public static NativeDaemonEvent parseRawEvent(String rawEvent) {
+ final int splitIndex = rawEvent.indexOf(' ');
+ if (splitIndex == -1) {
+ throw new IllegalArgumentException("unable to find ' ' separator");
+ }
+
+ final int code;
+ try {
+ code = Integer.parseInt(rawEvent.substring(0, splitIndex));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("problem parsing code", e);
+ }
+
+ final String message = rawEvent.substring(splitIndex + 1);
+ return new NativeDaemonEvent(code, message, rawEvent);
+ }
+
+ /**
+ * Filter the given {@link NativeDaemonEvent} list, returning
+ * {@link #getMessage()} for any events matching the requested code.
+ */
+ public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
+ final ArrayList<String> result = Lists.newArrayList();
+ for (NativeDaemonEvent event : events) {
+ if (event.getCode() == matchCode) {
+ result.add(event.getMessage());
+ }
+ }
+ return result.toArray(new String[result.size()]);
+ }
+}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 75e5366..7bb7938 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,19 +16,27 @@
package com.android.server;
-import static android.Manifest.permission.ACCESS_NETWORK_STATE;
-import static android.Manifest.permission.CHANGE_NETWORK_STATE;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
-import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.Manifest.permission.SHUTDOWN;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Secure.NETSTATS_ENABLED;
+import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceRxThrottleResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceTxThrottleResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.IpFwdStatusResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TetherDnsFwdTgtListResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TetherInterfaceListResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
@@ -37,8 +45,9 @@ import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.os.Binder;
import android.os.INetworkManagementService;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -47,6 +56,7 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import com.android.internal.net.NetworkStatsFactory;
+import com.android.server.NativeDaemonConnector.Command;
import com.google.android.collect.Sets;
import java.io.BufferedReader;
@@ -78,8 +88,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private static final boolean DBG = false;
private static final String NETD_TAG = "NetdConnector";
- private static final int ADD = 1;
- private static final int REMOVE = 2;
+ private static final String ADD = "add";
+ private static final String REMOVE = "remove";
private static final String DEFAULT = "default";
private static final String SECONDARY = "secondary";
@@ -125,8 +135,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private Thread mThread;
private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
- // TODO: replace with RemoteCallbackList
- private ArrayList<INetworkManagementEventObserver> mObservers;
+ private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
+ new RemoteCallbackList<INetworkManagementEventObserver>();
private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
@@ -147,14 +157,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub
*/
private NetworkManagementService(Context context) {
mContext = context;
- mObservers = new ArrayList<INetworkManagementEventObserver>();
if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
return;
}
mConnector = new NativeDaemonConnector(
- new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
+ new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 50);
mThread = new Thread(mConnector, NETD_TAG);
// Add ourself to the Watchdog monitors.
@@ -181,7 +190,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
if (hasKernelSupport && shouldEnable) {
Slog.d(TAG, "enabling bandwidth control");
try {
- mConnector.doCommand("bandwidth enable");
+ mConnector.execute("bandwidth", "enable");
mBandwidthControlEnabled = true;
} catch (NativeDaemonConnectorException e) {
Log.wtf(TAG, "problem enabling bandwidth controls", e);
@@ -193,27 +202,30 @@ public class NetworkManagementService extends INetworkManagementService.Stub
SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
}
- public void registerObserver(INetworkManagementEventObserver obs) {
- Slog.d(TAG, "Registering observer");
- mObservers.add(obs);
+ @Override
+ public void registerObserver(INetworkManagementEventObserver observer) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ mObservers.register(observer);
}
- public void unregisterObserver(INetworkManagementEventObserver obs) {
- Slog.d(TAG, "Unregistering observer");
- mObservers.remove(mObservers.indexOf(obs));
+ @Override
+ public void unregisterObserver(INetworkManagementEventObserver observer) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ mObservers.unregister(observer);
}
/**
* Notify our observers of an interface status change
*/
private void notifyInterfaceStatusChanged(String iface, boolean up) {
- for (INetworkManagementEventObserver obs : mObservers) {
+ final int length = mObservers.beginBroadcast();
+ for (int i = 0; i < length; i++) {
try {
- obs.interfaceStatusChanged(iface, up);
- } catch (Exception ex) {
- Slog.w(TAG, "Observer notifier failed", ex);
+ mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
+ } catch (RemoteException e) {
}
}
+ mObservers.finishBroadcast();
}
/**
@@ -221,26 +233,28 @@ public class NetworkManagementService extends INetworkManagementService.Stub
* (typically, an Ethernet cable has been plugged-in or unplugged).
*/
private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
- for (INetworkManagementEventObserver obs : mObservers) {
+ final int length = mObservers.beginBroadcast();
+ for (int i = 0; i < length; i++) {
try {
- obs.interfaceLinkStateChanged(iface, up);
- } catch (Exception ex) {
- Slog.w(TAG, "Observer notifier failed", ex);
+ mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
+ } catch (RemoteException e) {
}
}
+ mObservers.finishBroadcast();
}
/**
* Notify our observers of an interface addition.
*/
private void notifyInterfaceAdded(String iface) {
- for (INetworkManagementEventObserver obs : mObservers) {
+ final int length = mObservers.beginBroadcast();
+ for (int i = 0; i < length; i++) {
try {
- obs.interfaceAdded(iface);
- } catch (Exception ex) {
- Slog.w(TAG, "Observer notifier failed", ex);
+ mObservers.getBroadcastItem(i).interfaceAdded(iface);
+ } catch (RemoteException e) {
}
}
+ mObservers.finishBroadcast();
}
/**
@@ -252,33 +266,34 @@ public class NetworkManagementService extends INetworkManagementService.Stub
mActiveAlertIfaces.remove(iface);
mActiveQuotaIfaces.remove(iface);
- for (INetworkManagementEventObserver obs : mObservers) {
+ final int length = mObservers.beginBroadcast();
+ for (int i = 0; i < length; i++) {
try {
- obs.interfaceRemoved(iface);
- } catch (Exception ex) {
- Slog.w(TAG, "Observer notifier failed", ex);
+ mObservers.getBroadcastItem(i).interfaceRemoved(iface);
+ } catch (RemoteException e) {
}
}
+ mObservers.finishBroadcast();
}
/**
* Notify our observers of a limit reached.
*/
private void notifyLimitReached(String limitName, String iface) {
- for (INetworkManagementEventObserver obs : mObservers) {
+ final int length = mObservers.beginBroadcast();
+ for (int i = 0; i < length; i++) {
try {
- obs.limitReached(limitName, iface);
- } catch (Exception ex) {
- Slog.w(TAG, "Observer notifier failed", ex);
+ mObservers.getBroadcastItem(i).limitReached(limitName, iface);
+ } catch (RemoteException e) {
}
}
+ mObservers.finishBroadcast();
}
/**
* Let us know the daemon is connected
*/
protected void onDaemonConnected() {
- if (DBG) Slog.d(TAG, "onConnected");
mConnectedSignal.countDown();
}
@@ -351,233 +366,188 @@ public class NetworkManagementService extends INetworkManagementService.Stub
// INetworkManagementService members
//
- public String[] listInterfaces() throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
-
+ @Override
+ public String[] listInterfaces() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+ return NativeDaemonEvent.filterMessageList(
+ mConnector.executeForList("interface", "list"), InterfaceListResult);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Cannot communicate with native daemon to list interfaces");
+ throw e.rethrowAsParcelableException();
}
}
- public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
- String rsp;
+ @Override
+ public InterfaceConfiguration getInterfaceConfig(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ final NativeDaemonEvent event;
try {
- rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+ event = mConnector.execute("interface", "getcfg", iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Cannot communicate with native daemon to get interface config");
+ throw e.rethrowAsParcelableException();
}
- Slog.d(TAG, String.format("rsp <%s>", rsp));
- // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3]
- StringTokenizer st = new StringTokenizer(rsp);
+ event.checkCode(InterfaceGetCfgResult);
+
+ // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz flag1 flag2 flag3
+ final StringTokenizer st = new StringTokenizer(event.getMessage());
InterfaceConfiguration cfg;
try {
- try {
- int code = Integer.parseInt(st.nextToken(" "));
- if (code != NetdResponseCode.InterfaceGetCfgResult) {
- throw new IllegalStateException(
- String.format("Expected code %d, but got %d",
- NetdResponseCode.InterfaceGetCfgResult, code));
- }
- } catch (NumberFormatException nfe) {
- throw new IllegalStateException(
- String.format("Invalid response from daemon (%s)", rsp));
- }
-
cfg = new InterfaceConfiguration();
- cfg.hwAddr = st.nextToken(" ");
+ cfg.setHardwareAddress(st.nextToken(" "));
InetAddress addr = null;
int prefixLength = 0;
try {
- addr = NetworkUtils.numericToInetAddress(st.nextToken(" "));
+ addr = NetworkUtils.numericToInetAddress(st.nextToken());
} catch (IllegalArgumentException iae) {
Slog.e(TAG, "Failed to parse ipaddr", iae);
}
try {
- prefixLength = Integer.parseInt(st.nextToken(" "));
+ prefixLength = Integer.parseInt(st.nextToken());
} catch (NumberFormatException nfe) {
Slog.e(TAG, "Failed to parse prefixLength", nfe);
}
- cfg.addr = new LinkAddress(addr, prefixLength);
- cfg.interfaceFlags = st.nextToken("]").trim() +"]";
+ cfg.setLinkAddress(new LinkAddress(addr, prefixLength));
+ while (st.hasMoreTokens()) {
+ cfg.setFlag(st.nextToken());
+ }
} catch (NoSuchElementException nsee) {
- throw new IllegalStateException(
- String.format("Invalid response from daemon (%s)", rsp));
+ throw new IllegalStateException("Invalid response from daemon: " + event);
}
- Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
return cfg;
}
- public void setInterfaceConfig(
- String iface, InterfaceConfiguration cfg) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
- LinkAddress linkAddr = cfg.addr;
+ @Override
+ public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ LinkAddress linkAddr = cfg.getLinkAddress();
if (linkAddr == null || linkAddr.getAddress() == null) {
throw new IllegalStateException("Null LinkAddress given");
}
- String cmd = String.format("interface setcfg %s %s %d %s", iface,
+
+ final Command cmd = new Command("interface", "setcfg", iface,
linkAddr.getAddress().getHostAddress(),
- linkAddr.getNetworkPrefixLength(),
- cfg.interfaceFlags);
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate with native daemon to interface setcfg - " + e);
+ linkAddr.getNetworkPrefixLength());
+ for (String flag : cfg.getFlags()) {
+ cmd.appendArg(flag);
}
- }
- public void setInterfaceDown(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
try {
- InterfaceConfiguration ifcg = getInterfaceConfig(iface);
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
- setInterfaceConfig(iface, ifcg);
+ mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate with native daemon for interface down - " + e);
+ throw e.rethrowAsParcelableException();
}
}
- public void setInterfaceUp(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
- try {
- InterfaceConfiguration ifcg = getInterfaceConfig(iface);
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
- setInterfaceConfig(iface, ifcg);
- } catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate with native daemon for interface up - " + e);
- }
+ @Override
+ public void setInterfaceDown(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
+ ifcg.setInterfaceDown();
+ setInterfaceConfig(iface, ifcg);
}
- public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
- String cmd = String.format("interface ipv6privacyextensions %s %s", iface,
- enable ? "enable" : "disable");
+ @Override
+ public void setInterfaceUp(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
+ ifcg.setInterfaceUp();
+ setInterfaceConfig(iface, ifcg);
+ }
+
+ @Override
+ public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand(cmd);
+ mConnector.execute(
+ "interface", "ipv6privacyextensions", iface, enable ? "enable" : "disable");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate with native daemon to set ipv6privacyextensions - " + e);
+ throw e.rethrowAsParcelableException();
}
}
-
-
/* TODO: This is right now a IPv4 only function. Works for wifi which loses its
IPv6 addresses on interface down, but we need to do full clean up here */
- public void clearInterfaceAddresses(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
- String cmd = String.format("interface clearaddrs %s", iface);
+ @Override
+ public void clearInterfaceAddresses(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand(cmd);
+ mConnector.execute("interface", "clearaddrs", iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate with native daemon to interface clearallips - " + e);
+ throw e.rethrowAsParcelableException();
}
}
- public void enableIpv6(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void enableIpv6(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand(String.format("interface ipv6 %s enable", iface));
+ mConnector.execute("interface", "ipv6", iface, "enable");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon for enabling ipv6");
+ throw e.rethrowAsParcelableException();
}
}
- public void disableIpv6(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void disableIpv6(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand(String.format("interface ipv6 %s disable", iface));
+ mConnector.execute("interface", "ipv6", iface, "disable");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon for disabling ipv6");
+ throw e.rethrowAsParcelableException();
}
}
+ @Override
public void addRoute(String interfaceName, RouteInfo route) {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
modifyRoute(interfaceName, ADD, route, DEFAULT);
}
+ @Override
public void removeRoute(String interfaceName, RouteInfo route) {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
modifyRoute(interfaceName, REMOVE, route, DEFAULT);
}
+ @Override
public void addSecondaryRoute(String interfaceName, RouteInfo route) {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
modifyRoute(interfaceName, ADD, route, SECONDARY);
}
+ @Override
public void removeSecondaryRoute(String interfaceName, RouteInfo route) {
- mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
modifyRoute(interfaceName, REMOVE, route, SECONDARY);
}
- private void modifyRoute(String interfaceName, int action, RouteInfo route, String type) {
- ArrayList<String> rsp;
-
- StringBuilder cmd;
-
- switch (action) {
- case ADD:
- {
- cmd = new StringBuilder("interface route add " + interfaceName + " " + type);
- break;
- }
- case REMOVE:
- {
- cmd = new StringBuilder("interface route remove " + interfaceName + " " + type);
- break;
- }
- default:
- throw new IllegalStateException("Unknown action type " + action);
- }
+ private void modifyRoute(String interfaceName, String action, RouteInfo route, String type) {
+ final Command cmd = new Command("interface", "route", action, interfaceName, type);
// create triplet: dest-ip-addr prefixlength gateway-ip-addr
- LinkAddress la = route.getDestination();
- cmd.append(' ');
- cmd.append(la.getAddress().getHostAddress());
- cmd.append(' ');
- cmd.append(la.getNetworkPrefixLength());
- cmd.append(' ');
+ final LinkAddress la = route.getDestination();
+ cmd.appendArg(la.getAddress().getHostAddress());
+ cmd.appendArg(la.getNetworkPrefixLength());
+
if (route.getGateway() == null) {
if (la.getAddress() instanceof Inet4Address) {
- cmd.append("0.0.0.0");
+ cmd.appendArg("0.0.0.0");
} else {
- cmd.append ("::0");
+ cmd.appendArg("::0");
}
} else {
- cmd.append(route.getGateway().getHostAddress());
+ cmd.appendArg(route.getGateway().getHostAddress());
}
+
try {
- rsp = mConnector.doCommand(cmd.toString());
+ mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate with native dameon to add routes - "
- + e);
- }
-
- if (DBG) {
- for (String line : rsp) {
- Log.v(TAG, "add route response is " + line);
- }
+ throw e.rethrowAsParcelableException();
}
}
@@ -609,8 +579,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return list;
}
+ @Override
public RouteInfo[] getRoutes(String interfaceName) {
- mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
// v4 routes listed as:
@@ -680,308 +651,248 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
}
- return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
+ return routes.toArray(new RouteInfo[routes.size()]);
}
+ @Override
public void shutdown() {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.SHUTDOWN)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires SHUTDOWN permission");
- }
+ // TODO: remove from aidl if nobody calls externally
+ mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
Slog.d(TAG, "Shutting down");
}
+ @Override
public boolean getIpForwardingEnabled() throws IllegalStateException{
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- ArrayList<String> rsp;
+ final NativeDaemonEvent event;
try {
- rsp = mConnector.doCommand("ipfwd status");
+ event = mConnector.execute("ipfwd", "status");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate with native daemon to ipfwd status");
+ throw e.rethrowAsParcelableException();
}
- for (String line : rsp) {
- String[] tok = line.split(" ");
- if (tok.length < 3) {
- Slog.e(TAG, "Malformed response from native daemon: " + line);
- return false;
- }
-
- int code = Integer.parseInt(tok[0]);
- if (code == NetdResponseCode.IpFwdStatusResult) {
- // 211 Forwarding <enabled/disabled>
- return "enabled".equals(tok[2]);
- } else {
- throw new IllegalStateException(String.format("Unexpected response code %d", code));
- }
- }
- throw new IllegalStateException("Got an empty response");
+ // 211 Forwarding enabled
+ event.checkCode(IpFwdStatusResult);
+ return event.getMessage().endsWith("enabled");
}
- public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
+ @Override
+ public void setIpForwardingEnabled(boolean enable) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("ipfwd", enable ? "enable" : "disable");
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
}
- public void startTethering(String[] dhcpRange)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void startTethering(String[] dhcpRange) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// cmd is "tether start first_start first_stop second_start second_stop ..."
// an odd number of addrs will fail
- String cmd = "tether start";
+
+ final Command cmd = new Command("tether", "start");
for (String d : dhcpRange) {
- cmd += " " + d;
+ cmd.appendArg(d);
}
try {
- mConnector.doCommand(cmd);
+ mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Unable to communicate to native daemon");
+ throw e.rethrowAsParcelableException();
}
}
- public void stopTethering() throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void stopTethering() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand("tether stop");
+ mConnector.execute("tether", "stop");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
+ throw e.rethrowAsParcelableException();
}
}
- public boolean isTetheringStarted() throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public boolean isTetheringStarted() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- ArrayList<String> rsp;
+ final NativeDaemonEvent event;
try {
- rsp = mConnector.doCommand("tether status");
+ event = mConnector.execute("tether", "status");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon to get tether status");
+ throw e.rethrowAsParcelableException();
}
- for (String line : rsp) {
- String[] tok = line.split(" ");
- if (tok.length < 3) {
- throw new IllegalStateException("Malformed response for tether status: " + line);
- }
- int code = Integer.parseInt(tok[0]);
- if (code == NetdResponseCode.TetherStatusResult) {
- // XXX: Tethering services <started/stopped> <TBD>...
- return "started".equals(tok[2]);
- } else {
- throw new IllegalStateException(String.format("Unexpected response code %d", code));
- }
- }
- throw new IllegalStateException("Got an empty response");
+ // 210 Tethering services started
+ event.checkCode(TetherStatusResult);
+ return event.getMessage().endsWith("started");
}
- public void tetherInterface(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void tetherInterface(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand("tether interface add " + iface);
+ mConnector.execute("tether", "interface", "add", iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon for adding tether interface");
+ throw e.rethrowAsParcelableException();
}
}
+ @Override
public void untetherInterface(String iface) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand("tether interface remove " + iface);
+ mConnector.execute("tether", "interface", "remove", iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon for removing tether interface");
+ throw e.rethrowAsParcelableException();
}
}
- public String[] listTetheredInterfaces() throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public String[] listTetheredInterfaces() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return mConnector.doListCommand(
- "tether interface list", NetdResponseCode.TetherInterfaceListResult);
+ return NativeDaemonEvent.filterMessageList(
+ mConnector.executeForList("tether", "interface", "list"),
+ TetherInterfaceListResult);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon for listing tether interfaces");
+ throw e.rethrowAsParcelableException();
}
}
- public void setDnsForwarders(String[] dns) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void setDnsForwarders(String[] dns) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ final Command cmd = new Command("tether", "dns", "set");
+ for (String s : dns) {
+ cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
+ }
+
try {
- String cmd = "tether dns set";
- for (String s : dns) {
- cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress();
- }
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon for setting tether dns");
- }
- } catch (IllegalArgumentException e) {
- throw new IllegalStateException("Error resolving dns name", e);
+ mConnector.execute(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
}
}
- public String[] getDnsForwarders() throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public String[] getDnsForwarders() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return mConnector.doListCommand(
- "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
+ return NativeDaemonEvent.filterMessageList(
+ mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon for listing tether dns");
+ throw e.rethrowAsParcelableException();
}
}
- private void modifyNat(String cmd, String internalInterface, String externalInterface)
+ private void modifyNat(String action, String internalInterface, String externalInterface)
throws SocketException {
- cmd = String.format("nat %s %s %s", cmd, internalInterface, externalInterface);
+ final Command cmd = new Command("nat", action, internalInterface, externalInterface);
- NetworkInterface internalNetworkInterface =
- NetworkInterface.getByName(internalInterface);
+ final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
+ internalInterface);
if (internalNetworkInterface == null) {
- cmd += " 0";
+ cmd.appendArg("0");
} else {
- Collection<InterfaceAddress>interfaceAddresses =
- internalNetworkInterface.getInterfaceAddresses();
- cmd += " " + interfaceAddresses.size();
+ Collection<InterfaceAddress> interfaceAddresses = internalNetworkInterface
+ .getInterfaceAddresses();
+ cmd.appendArg(interfaceAddresses.size());
for (InterfaceAddress ia : interfaceAddresses) {
- InetAddress addr = NetworkUtils.getNetworkPart(ia.getAddress(),
- ia.getNetworkPrefixLength());
- cmd = cmd + " " + addr.getHostAddress() + "/" + ia.getNetworkPrefixLength();
+ InetAddress addr = NetworkUtils.getNetworkPart(
+ ia.getAddress(), ia.getNetworkPrefixLength());
+ cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
}
}
- mConnector.doCommand(cmd);
+ try {
+ mConnector.execute(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
}
- public void enableNat(String internalInterface, String externalInterface)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- if (DBG) Log.d(TAG, "enableNat(" + internalInterface + ", " + externalInterface + ")");
+ @Override
+ public void enableNat(String internalInterface, String externalInterface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
modifyNat("enable", internalInterface, externalInterface);
- } catch (Exception e) {
- Log.e(TAG, "enableNat got Exception " + e.toString());
- throw new IllegalStateException(
- "Unable to communicate to native daemon for enabling NAT interface");
+ } catch (SocketException e) {
+ throw new IllegalStateException(e);
}
}
- public void disableNat(String internalInterface, String externalInterface)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- if (DBG) Log.d(TAG, "disableNat(" + internalInterface + ", " + externalInterface + ")");
+ @Override
+ public void disableNat(String internalInterface, String externalInterface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
modifyNat("disable", internalInterface, externalInterface);
- } catch (Exception e) {
- Log.e(TAG, "disableNat got Exception " + e.toString());
- throw new IllegalStateException(
- "Unable to communicate to native daemon for disabling NAT interface");
+ } catch (SocketException e) {
+ throw new IllegalStateException(e);
}
}
- public String[] listTtys() throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public String[] listTtys() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
+ return NativeDaemonEvent.filterMessageList(
+ mConnector.executeForList("list_ttys"), TtyListResult);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Unable to communicate to native daemon for listing TTYs");
+ throw e.rethrowAsParcelableException();
}
}
- public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
- String dns2Addr) throws IllegalStateException {
+ @Override
+ public void attachPppd(
+ String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
+ mConnector.execute("pppd", "attach", tty,
NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
- NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress()));
- } catch (IllegalArgumentException e) {
- throw new IllegalStateException("Error resolving addr", e);
+ NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
+ throw e.rethrowAsParcelableException();
}
}
- public void detachPppd(String tty) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void detachPppd(String tty) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand(String.format("pppd detach %s", tty));
+ mConnector.execute("pppd", "detach", tty);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
+ throw e.rethrowAsParcelableException();
}
}
- public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+ @Override
+ public void startAccessPoint(
+ WifiConfiguration wifiConfig, String wlanIface, String softapIface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
wifiFirmwareReload(wlanIface, "AP");
- mConnector.doCommand(String.format("softap start " + wlanIface));
+ mConnector.execute("softap", "start", wlanIface);
if (wifiConfig == null) {
- mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ mConnector.execute("softap", "set", wlanIface, softapIface);
} else {
- /**
- * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
- * argv1 - wlan interface
- * argv2 - softap interface
- * argv3 - SSID
- * argv4 - Security
- * argv5 - Key
- * argv6 - Channel
- * argv7 - Preamble
- * argv8 - Max SCB
- */
- String str = String.format("softap set " + wlanIface + " " + softapIface +
- " %s %s %s", convertQuotedString(wifiConfig.SSID),
- getSecurityType(wifiConfig),
- convertQuotedString(wifiConfig.preSharedKey));
- mConnector.doCommand(str);
+ mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
+ getSecurityType(wifiConfig), wifiConfig.preSharedKey);
}
- mConnector.doCommand(String.format("softap startap"));
+ mConnector.execute("softap", "startap");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon to start softap", e);
+ throw e.rethrowAsParcelableException();
}
}
- private String convertQuotedString(String s) {
- if (s == null) {
- return s;
- }
- /* Replace \ with \\, then " with \" and add quotes at end */
- return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
- }
-
- private String getSecurityType(WifiConfiguration wifiConfig) {
+ private static String getSecurityType(WifiConfiguration wifiConfig) {
switch (wifiConfig.getAuthType()) {
case KeyMgmt.WPA_PSK:
return "wpa-psk";
@@ -993,113 +904,58 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
/* @param mode can be "AP", "STA" or "P2P" */
- public void wifiFirmwareReload(String wlanIface, String mode) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
-
+ @Override
+ public void wifiFirmwareReload(String wlanIface, String mode) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand(String.format("softap fwreload " + wlanIface + " " + mode));
+ mConnector.execute("softap", "fwreload", wlanIface, mode);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon ", e);
+ throw e.rethrowAsParcelableException();
}
}
- public void stopAccessPoint(String wlanIface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+ @Override
+ public void stopAccessPoint(String wlanIface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand("softap stopap");
- mConnector.doCommand("softap stop " + wlanIface);
+ mConnector.execute("softap", "stopap");
+ mConnector.execute("softap", "stop", wlanIface);
wifiFirmwareReload(wlanIface, "STA");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
- e);
+ throw e.rethrowAsParcelableException();
}
}
- public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
+ @Override
+ public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
if (wifiConfig == null) {
- mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ mConnector.execute("softap", "set", wlanIface, softapIface);
} else {
- String str = String.format("softap set " + wlanIface + " " + softapIface
- + " %s %s %s", convertQuotedString(wifiConfig.SSID),
- getSecurityType(wifiConfig),
- convertQuotedString(wifiConfig.preSharedKey));
- mConnector.doCommand(str);
+ mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
+ getSecurityType(wifiConfig), wifiConfig.preSharedKey);
}
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon to set soft AP",
- e);
- }
- }
-
- private long getInterfaceCounter(String iface, boolean rx) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- try {
- String rsp;
- try {
- rsp = mConnector.doCommand(
- String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
- } catch (NativeDaemonConnectorException e1) {
- Slog.e(TAG, "Error communicating with native daemon", e1);
- return -1;
- }
-
- String[] tok = rsp.split(" ");
- if (tok.length < 2) {
- Slog.e(TAG, String.format("Malformed response for reading %s interface",
- (rx ? "rx" : "tx")));
- return -1;
- }
-
- int code;
- try {
- code = Integer.parseInt(tok[0]);
- } catch (NumberFormatException nfe) {
- Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
- return -1;
- }
- if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
- !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
- Slog.e(TAG, String.format("Unexpected response code %d", code));
- return -1;
- }
- return Long.parseLong(tok[1]);
- } catch (Exception e) {
- Slog.e(TAG, String.format(
- "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
+ throw e.rethrowAsParcelableException();
}
- return -1;
}
@Override
public NetworkStats getNetworkStatsSummary() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
return mStatsFactory.readNetworkStatsSummary();
}
@Override
public NetworkStats getNetworkStatsDetail() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
return mStatsFactory.readNetworkStatsDetail(UID_ALL);
}
@Override
public void setInterfaceQuota(String iface, long quotaBytes) {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// silently discard when control disabled
// TODO: eventually migrate to be always enabled
@@ -1110,22 +966,19 @@ public class NetworkManagementService extends INetworkManagementService.Stub
throw new IllegalStateException("iface " + iface + " already has quota");
}
- final StringBuilder command = new StringBuilder();
- command.append("bandwidth setiquota ").append(iface).append(" ").append(quotaBytes);
-
try {
// TODO: support quota shared across interfaces
- mConnector.doCommand(command.toString());
+ mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
mActiveQuotaIfaces.add(iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon", e);
+ throw e.rethrowAsParcelableException();
}
}
}
@Override
public void removeInterfaceQuota(String iface) {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// silently discard when control disabled
// TODO: eventually migrate to be always enabled
@@ -1137,25 +990,21 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return;
}
- 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());
+ mConnector.execute("bandwidth", "removeiquota", iface);
} catch (NativeDaemonConnectorException e) {
- // TODO: include current iptables state
- throw new IllegalStateException("Error communicating to native daemon", e);
+ throw e.rethrowAsParcelableException();
}
}
}
@Override
public void setInterfaceAlert(String iface, long alertBytes) {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// silently discard when control disabled
// TODO: eventually migrate to be always enabled
@@ -1171,23 +1020,19 @@ public class NetworkManagementService extends INetworkManagementService.Stub
throw new IllegalStateException("iface " + iface + " already has alert");
}
- final StringBuilder command = new StringBuilder();
- command.append("bandwidth setinterfacealert ").append(iface).append(" ").append(
- alertBytes);
-
try {
// TODO: support alert shared across interfaces
- mConnector.doCommand(command.toString());
+ mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
mActiveAlertIfaces.add(iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon", e);
+ throw e.rethrowAsParcelableException();
}
}
}
@Override
public void removeInterfaceAlert(String iface) {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// silently discard when control disabled
// TODO: eventually migrate to be always enabled
@@ -1199,40 +1044,34 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return;
}
- final StringBuilder command = new StringBuilder();
- command.append("bandwidth removeinterfacealert ").append(iface);
-
try {
// TODO: support alert shared across interfaces
- mConnector.doCommand(command.toString());
+ mConnector.execute("bandwidth", "removeinterfacealert", iface);
mActiveAlertIfaces.remove(iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon", e);
+ throw e.rethrowAsParcelableException();
}
}
}
@Override
public void setGlobalAlert(long alertBytes) {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// silently discard when control disabled
// TODO: eventually migrate to be always enabled
if (!mBandwidthControlEnabled) return;
- final StringBuilder command = new StringBuilder();
- command.append("bandwidth setglobalalert ").append(alertBytes);
-
try {
- mConnector.doCommand(command.toString());
+ mConnector.execute("bandwidth", "setglobalalert", alertBytes);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon", e);
+ throw e.rethrowAsParcelableException();
}
}
@Override
public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// silently discard when control disabled
// TODO: eventually migrate to be always enabled
@@ -1245,47 +1084,35 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return;
}
- final StringBuilder command = new StringBuilder();
- command.append("bandwidth");
- if (rejectOnQuotaInterfaces) {
- command.append(" addnaughtyapps");
- } else {
- command.append(" removenaughtyapps");
- }
- command.append(" ").append(uid);
-
try {
- mConnector.doCommand(command.toString());
+ mConnector.execute("bandwidth",
+ rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
if (rejectOnQuotaInterfaces) {
mUidRejectOnQuota.put(uid, true);
} else {
mUidRejectOnQuota.delete(uid);
}
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon", e);
+ throw e.rethrowAsParcelableException();
}
}
}
@Override
public boolean isBandwidthControlEnabled() {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
return mBandwidthControlEnabled;
}
@Override
public NetworkStats getNetworkStatsUidDetail(int uid) {
- if (Binder.getCallingUid() != uid) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- }
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
return mStatsFactory.readNetworkStatsDetail(uid);
}
@Override
public NetworkStats getNetworkStatsTethering(String[] ifacePairs) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
if (ifacePairs.length % 2 != 0) {
throw new IllegalArgumentException(
@@ -1304,33 +1131,19 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
private NetworkStats.Entry getNetworkStatsTethering(String ifaceIn, String ifaceOut) {
- final StringBuilder command = new StringBuilder();
- command.append("bandwidth gettetherstats ").append(ifaceIn).append(" ").append(ifaceOut);
-
- final String rsp;
+ final NativeDaemonEvent event;
try {
- rsp = mConnector.doCommand(command.toString()).get(0);
+ event = mConnector.execute("bandwidth", "gettetherstats", ifaceIn, ifaceOut);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException("Error communicating to native daemon", e);
+ throw e.rethrowAsParcelableException();
}
- final String[] tok = rsp.split(" ");
- /* Expecting: "code ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets" */
- if (tok.length != 7) {
- throw new IllegalStateException("Native daemon returned unexpected result: " + rsp);
- }
+ event.checkCode(TetheringStatsResult);
- final int code;
- try {
- code = Integer.parseInt(tok[0]);
- } catch (NumberFormatException e) {
- throw new IllegalStateException(
- "Failed to parse native daemon return code for " + ifaceIn + " " + ifaceOut);
- }
- if (code != NetdResponseCode.TetheringStatsResult) {
- throw new IllegalStateException(
- "Unexpected return code from native daemon for " + ifaceIn + " " + ifaceOut);
- }
+ // 221 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
+ final StringTokenizer tok = new StringTokenizer(event.getMessage());
+ tok.nextToken();
+ tok.nextToken();
try {
final NetworkStats.Entry entry = new NetworkStats.Entry();
@@ -1338,10 +1151,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
entry.uid = UID_TETHERING;
entry.set = SET_DEFAULT;
entry.tag = TAG_NONE;
- entry.rxBytes = Long.parseLong(tok[3]);
- entry.rxPackets = Long.parseLong(tok[4]);
- entry.txBytes = Long.parseLong(tok[5]);
- entry.txPackets = Long.parseLong(tok[6]);
+ entry.rxBytes = Long.parseLong(tok.nextToken());
+ entry.rxPackets = Long.parseLong(tok.nextToken());
+ entry.txBytes = Long.parseLong(tok.nextToken());
+ entry.txPackets = Long.parseLong(tok.nextToken());
return entry;
} catch (NumberFormatException e) {
throw new IllegalStateException(
@@ -1349,122 +1162,95 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
+ @Override
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.doCommand(String.format(
- "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
+ mConnector.execute("interface", "setthrottle", iface, rxKbps, txKbps);
} catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
+ throw e.rethrowAsParcelableException();
}
}
private int getInterfaceThrottle(String iface, boolean rx) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ final NativeDaemonEvent event;
try {
- String rsp;
- try {
- rsp = mConnector.doCommand(
- String.format("interface getthrottle %s %s", iface,
- (rx ? "rx" : "tx"))).get(0);
- } catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
- return -1;
- }
+ event = mConnector.execute("interface", "getthrottle", iface, rx ? "rx" : "tx");
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
- String[] tok = rsp.split(" ");
- if (tok.length < 2) {
- Slog.e(TAG, "Malformed response to getthrottle command");
- return -1;
- }
+ if (rx) {
+ event.checkCode(InterfaceRxThrottleResult);
+ } else {
+ event.checkCode(InterfaceTxThrottleResult);
+ }
- int code;
- try {
- code = Integer.parseInt(tok[0]);
- } catch (NumberFormatException nfe) {
- Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
- return -1;
- }
- if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
- !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
- Slog.e(TAG, String.format("Unexpected response code %d", code));
- return -1;
- }
- return Integer.parseInt(tok[1]);
- } catch (Exception e) {
- Slog.e(TAG, String.format(
- "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
+ try {
+ return Integer.parseInt(event.getMessage());
+ } catch (NumberFormatException e) {
+ throw new IllegalStateException("unexpected response:" + event);
}
- return -1;
}
+ @Override
public int getInterfaceRxThrottle(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
return getInterfaceThrottle(iface, true);
}
+ @Override
public int getInterfaceTxThrottle(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
return getInterfaceThrottle(iface, false);
}
- public void setDefaultInterfaceForDns(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void setDefaultInterfaceForDns(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- String cmd = "resolver setdefaultif " + iface;
-
- mConnector.doCommand(cmd);
+ mConnector.execute("resolver", "setdefaultif", iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Error communicating with native daemon to set default interface", e);
+ throw e.rethrowAsParcelableException();
}
}
- public void setDnsServersForInterface(String iface, String[] servers)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
- "NetworkManagementService");
- try {
- String cmd = "resolver setifdns " + iface;
- for (String s : servers) {
- InetAddress a = NetworkUtils.numericToInetAddress(s);
- if (a.isAnyLocalAddress() == false) {
- cmd += " " + a.getHostAddress();
- }
+ @Override
+ public void setDnsServersForInterface(String iface, String[] servers) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ final Command cmd = new Command("resolver", "setifdns", iface);
+ for (String s : servers) {
+ InetAddress a = NetworkUtils.numericToInetAddress(s);
+ if (a.isAnyLocalAddress() == false) {
+ cmd.appendArg(a.getHostAddress());
}
- mConnector.doCommand(cmd);
- } catch (IllegalArgumentException e) {
- throw new IllegalStateException("Error setting dnsn for interface", e);
+ }
+
+ try {
+ mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Error communicating with native daemon to set dns for interface", e);
+ throw e.rethrowAsParcelableException();
}
}
- public void flushDefaultDnsCache() throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void flushDefaultDnsCache() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- String cmd = "resolver flushdefaultif";
-
- mConnector.doCommand(cmd);
+ mConnector.execute("resolver", "flushdefaultif");
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Error communicating with native deamon to flush default interface", e);
+ throw e.rethrowAsParcelableException();
}
}
- public void flushInterfaceDnsCache(String iface) throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ @Override
+ public void flushInterfaceDnsCache(String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- String cmd = "resolver flushif " + iface;
-
- mConnector.doCommand(cmd);
+ mConnector.execute("resolver", "flushif", iface);
} catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Error communicating with native daemon to flush interface " + iface, e);
+ throw e.rethrowAsParcelableException();
}
}
@@ -1479,6 +1265,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
+ pw.println("NetworkManagementService NativeDaemonConnector Log:");
+ mConnector.dump(fd, pw, args);
+ pw.println();
+
pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
synchronized (mQuotaLock) {
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index f7fe39e..a7d1992 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -234,8 +234,9 @@ public class NetworkTimeUpdateService {
String action = intent.getAction();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
// There is connectivity
- NetworkInfo netInfo = (NetworkInfo)intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
+ final ConnectivityManager connManager = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo netInfo = connManager.getActiveNetworkInfo();
if (netInfo != null) {
// Verify that it's a WIFI connection
if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 5039294..34a8a02 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -45,6 +45,7 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserId;
import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -1034,7 +1035,7 @@ public class NotificationManagerService extends INotificationManager.Stub
try {
ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
pkg, 0);
- if (ai.uid != uid) {
+ if (!UserId.isSameApp(ai.uid, uid)) {
throw new SecurityException("Calling uid " + uid + " gave package"
+ pkg + " which is owned by uid " + ai.uid);
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2a0d2a0..bb0ac3e 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -51,7 +51,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.WorkSource;
-import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
@@ -60,6 +59,7 @@ import android.view.WindowManagerPolicy;
import static android.provider.Settings.System.DIM_SCREEN;
import static android.provider.Settings.System.SCREEN_BRIGHTNESS;
import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE;
+import static android.provider.Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ;
import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN;
@@ -106,6 +106,14 @@ public class PowerManagerService extends IPowerManager.Stub
// light sensor events rate in microseconds
private static final int LIGHT_SENSOR_RATE = 1000000;
+ // Expansion of range of light values when applying scale from light
+ // sensor brightness setting, in the [0..255] brightness range.
+ private static final int LIGHT_SENSOR_RANGE_EXPANSION = 20;
+
+ // Scaling factor of the light sensor brightness setting when applying
+ // it to the final brightness.
+ private static final int LIGHT_SENSOR_OFFSET_SCALE = 8;
+
// For debouncing the proximity sensor in milliseconds
private static final int PROXIMITY_SENSOR_DELAY = 1000;
@@ -118,6 +126,9 @@ public class PowerManagerService extends IPowerManager.Stub
// Default timeout for screen off, if not found in settings database = 15 seconds.
private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000;
+ // Screen brightness should always have a value, but just in case...
+ private static final int DEFAULT_SCREEN_BRIGHTNESS = 192;
+
// flags for setPowerState
private static final int SCREEN_ON_BIT = 0x00000001;
private static final int SCREEN_BRIGHT_BIT = 0x00000002;
@@ -150,6 +161,8 @@ public class PowerManagerService extends IPowerManager.Stub
static final int ANIM_STEPS = 60/4;
// Slower animation for autobrightness changes
static final int AUTOBRIGHTNESS_ANIM_STEPS = 60;
+ // Number of steps when performing a more immediate brightness change.
+ static final int IMMEDIATE_ANIM_STEPS = 4;
// These magic numbers are the initial state of the LEDs at boot. Ideally
// we should read them from the driver, but our current hardware returns 0
@@ -227,6 +240,7 @@ public class PowerManagerService extends IPowerManager.Stub
private boolean mLightSensorPendingDecrease = false;
private boolean mLightSensorPendingIncrease = false;
private float mLightSensorPendingValue = -1;
+ private float mLightSensorAdjustSetting = 0;
private int mLightSensorScreenBrightness = -1;
private int mLightSensorButtonBrightness = -1;
private int mLightSensorKeyboardBrightness = -1;
@@ -240,6 +254,7 @@ public class PowerManagerService extends IPowerManager.Stub
// mLastScreenOnTime is the time the screen was last turned on
private long mLastScreenOnTime;
private boolean mPreventScreenOn;
+ private int mScreenBrightnessSetting = DEFAULT_SCREEN_BRIGHTNESS;
private int mScreenBrightnessOverride = -1;
private int mButtonBrightnessOverride = -1;
private int mScreenBrightnessDim;
@@ -460,6 +475,9 @@ public class PowerManagerService extends IPowerManager.Stub
// DIM_SCREEN
//mDimScreen = getInt(DIM_SCREEN) != 0;
+ mScreenBrightnessSetting = getInt(SCREEN_BRIGHTNESS, DEFAULT_SCREEN_BRIGHTNESS);
+ mLightSensorAdjustSetting = getFloat(SCREEN_AUTO_BRIGHTNESS_ADJ, 0);
+
// SCREEN_BRIGHTNESS_MODE, default to manual
setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL));
@@ -624,9 +642,12 @@ public class PowerManagerService extends IPowerManager.Stub
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ + Settings.System.NAME + "=?) or ("
+ + Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?)",
- new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN,
- SCREEN_BRIGHTNESS_MODE, WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE},
+ new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN, SCREEN_BRIGHTNESS,
+ SCREEN_BRIGHTNESS_MODE, SCREEN_AUTO_BRIGHTNESS_ADJ,
+ WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE},
null);
mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler);
SettingsObserver settingsObserver = new SettingsObserver();
@@ -1163,7 +1184,8 @@ public class PowerManagerService extends IPowerManager.Stub
pw.println(" mProximitySensorActive=" + mProximitySensorActive);
pw.println(" mProximityPendingValue=" + mProximityPendingValue);
pw.println(" mLastProximityEventTime=" + mLastProximityEventTime);
- pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
+ pw.println(" mLightSensorEnabled=" + mLightSensorEnabled
+ + " mLightSensorAdjustSetting=" + mLightSensorAdjustSetting);
pw.println(" mLightSensorValue=" + mLightSensorValue
+ " mLightSensorPendingValue=" + mLightSensorPendingValue);
pw.println(" mLightSensorPendingDecrease=" + mLightSensorPendingDecrease
@@ -1706,11 +1728,6 @@ public class PowerManagerService extends IPowerManager.Stub
// make sure button and key backlights are off too
mButtonLight.turnOff();
mKeyboardLight.turnOff();
- // clear current value so we will update based on the new conditions
- // when the sensor is reenabled.
- mLightSensorValue = -1;
- // reset our highest light sensor value when the screen turns off
- mHighestLightSensorValue = -1;
}
}
}
@@ -2235,20 +2252,15 @@ public class PowerManagerService extends IPowerManager.Stub
}
private int getPreferredBrightness() {
- try {
- if (mScreenBrightnessOverride >= 0) {
- return mScreenBrightnessOverride;
- } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness
- && mAutoBrightessEnabled) {
- return mLightSensorScreenBrightness;
- }
- final int brightness = Settings.System.getInt(mContext.getContentResolver(),
- SCREEN_BRIGHTNESS);
- // Don't let applications turn the screen all the way off
- return Math.max(brightness, mScreenBrightnessDim);
- } catch (SettingNotFoundException snfe) {
- return Power.BRIGHTNESS_ON;
+ if (mScreenBrightnessOverride >= 0) {
+ return mScreenBrightnessOverride;
+ } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness
+ && mAutoBrightessEnabled) {
+ return mLightSensorScreenBrightness;
}
+ final int brightness = mScreenBrightnessSetting;
+ // Don't let applications turn the screen all the way off
+ return Math.max(brightness, mScreenBrightnessDim);
}
private int applyButtonState(int state) {
@@ -2444,7 +2456,34 @@ public class PowerManagerService extends IPowerManager.Stub
break;
}
}
- return values[i];
+ // This is the range of brightness values that we can use.
+ final int minval = values[0];
+ final int maxval = values[mAutoBrightnessLevels.length];
+ // This is the range we will be scaling. We put some padding
+ // at the low and high end to give the adjustment a little better
+ // impact on the actual observed value.
+ final int range = (maxval-minval) + LIGHT_SENSOR_RANGE_EXPANSION;
+ // This is the desired brightness value from 0.0 to 1.0.
+ float valf = ((values[i]-minval+(LIGHT_SENSOR_RANGE_EXPANSION/2))/(float)range);
+ // Apply a scaling to the value based on the adjustment.
+ if (mLightSensorAdjustSetting > 0 && mLightSensorAdjustSetting <= 1) {
+ float adj = (float)Math.sqrt(1.0f-mLightSensorAdjustSetting);
+ if (adj <= .00001) {
+ valf = 1;
+ } else {
+ valf /= adj;
+ }
+ } else if (mLightSensorAdjustSetting < 0 && mLightSensorAdjustSetting >= -1) {
+ float adj = (float)Math.sqrt(1.0f+mLightSensorAdjustSetting);
+ valf *= adj;
+ }
+ // Apply an additional offset to the value based on the adjustment.
+ valf += mLightSensorAdjustSetting/LIGHT_SENSOR_OFFSET_SCALE;
+ // Convert the 0.0-1.0 value back to a brightness integer.
+ int val = (int)((valf*range)+minval) - (LIGHT_SENSOR_RANGE_EXPANSION/2);
+ if (val < minval) val = minval;
+ else if (val > maxval) val = maxval;
+ return val;
} catch (Exception e) {
// guard against null pointer or index out of bounds errors
Slog.e(TAG, "getAutoBrightnessValue", e);
@@ -2473,7 +2512,7 @@ public class PowerManagerService extends IPowerManager.Stub
int value = (int)mLightSensorPendingValue;
mLightSensorPendingDecrease = false;
mLightSensorPendingIncrease = false;
- lightSensorChangedLocked(value);
+ lightSensorChangedLocked(value, false);
}
}
}
@@ -2483,18 +2522,19 @@ public class PowerManagerService extends IPowerManager.Stub
synchronized (mLocks) {
mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED);
if (mIsDocked) {
+ // allow brightness to decrease when docked
mHighestLightSensorValue = -1;
}
if ((mPowerState & SCREEN_ON_BIT) != 0) {
// force lights recalculation
int value = (int)mLightSensorValue;
mLightSensorValue = -1;
- lightSensorChangedLocked(value);
+ lightSensorChangedLocked(value, false);
}
}
}
- private void lightSensorChangedLocked(int value) {
+ private void lightSensorChangedLocked(int value, boolean immediate) {
if (mDebugLightSensor) {
Slog.d(TAG, "lightSensorChangedLocked " + value);
}
@@ -2540,7 +2580,8 @@ public class PowerManagerService extends IPowerManager.Stub
if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) {
if (!mSkippedScreenOn) {
- mScreenBrightness.setTargetLocked(lcdValue, AUTOBRIGHTNESS_ANIM_STEPS,
+ mScreenBrightness.setTargetLocked(lcdValue,
+ immediate ? IMMEDIATE_ANIM_STEPS : AUTOBRIGHTNESS_ANIM_STEPS,
INITIAL_SCREEN_BRIGHTNESS, (int)mScreenBrightness.curValue);
}
}
@@ -2688,7 +2729,7 @@ public class PowerManagerService extends IPowerManager.Stub
if (mLightSensorValue >= 0) {
int value = (int)mLightSensorValue;
mLightSensorValue = -1;
- lightSensorChangedLocked(value);
+ lightSensorChangedLocked(value, false);
}
}
userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);
@@ -2948,10 +2989,28 @@ public class PowerManagerService extends IPowerManager.Stub
Binder.restoreCallingIdentity(identity);
}
- // update our animation state
- synchronized (mLocks) {
- mScreenBrightness.targetValue = brightness;
- mScreenBrightness.jumpToTargetLocked();
+ mScreenBrightness.targetValue = brightness;
+ mScreenBrightness.jumpToTargetLocked();
+ }
+ }
+
+ public void setAutoBrightnessAdjustment(float adj) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ synchronized (mLocks) {
+ mLightSensorAdjustSetting = adj;
+ if (mSensorManager != null && mLightSensorEnabled) {
+ // clear calling identity so sensor manager battery stats are accurate
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // force recompute of backlight values
+ if (mLightSensorValue >= 0) {
+ int value = (int)mLightSensorValue;
+ mLightSensorValue = -1;
+ handleLightSensorValue(value, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
}
@@ -3056,20 +3115,25 @@ public class PowerManagerService extends IPowerManager.Stub
}
if (mSensorManager != null && mLightSensorEnabled != enable) {
mLightSensorEnabled = enable;
- // clear previous values so we will adjust to current brightness when
- // auto-brightness is reenabled
- mHighestLightSensorValue = -1;
- mLightSensorValue = -1;
-
// clear calling identity so sensor manager battery stats are accurate
long identity = Binder.clearCallingIdentity();
try {
if (enable) {
+ // reset our highest value when reenabling
+ mHighestLightSensorValue = -1;
+ // force recompute of backlight values
+ if (mLightSensorValue >= 0) {
+ int value = (int)mLightSensorValue;
+ mLightSensorValue = -1;
+ handleLightSensorValue(value, true);
+ }
mSensorManager.registerListener(mLightListener, mLightSensor,
LIGHT_SENSOR_RATE);
} else {
mSensorManager.unregisterListener(mLightListener);
mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = false;
+ mLightSensorPendingIncrease = false;
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3121,43 +3185,45 @@ public class PowerManagerService extends IPowerManager.Stub
}
};
+ private void handleLightSensorValue(int value, boolean immediate) {
+ long milliseconds = SystemClock.elapsedRealtime();
+ if (mLightSensorValue == -1 ||
+ milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
+ // process the value immediately if screen has just turned on
+ mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = false;
+ mLightSensorPendingIncrease = false;
+ lightSensorChangedLocked(value, immediate);
+ } else {
+ if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
+ (value < mLightSensorValue && mLightSensorPendingIncrease) ||
+ (value == mLightSensorValue) ||
+ (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) {
+ // delay processing to debounce the sensor
+ mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = (value < mLightSensorValue);
+ mLightSensorPendingIncrease = (value > mLightSensorValue);
+ if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
+ mLightSensorPendingValue = value;
+ mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
+ }
+ } else {
+ mLightSensorPendingValue = value;
+ }
+ }
+ }
+
SensorEventListener mLightListener = new SensorEventListener() {
public void onSensorChanged(SensorEvent event) {
+ if (mDebugLightSensor) {
+ Slog.d(TAG, "onSensorChanged: light value: " + event.values[0]);
+ }
synchronized (mLocks) {
// ignore light sensor while screen is turning off
if (isScreenTurningOffLocked()) {
return;
}
-
- int value = (int)event.values[0];
- long milliseconds = SystemClock.elapsedRealtime();
- if (mDebugLightSensor) {
- Slog.d(TAG, "onSensorChanged: light value: " + value);
- }
- if (mLightSensorValue == -1 ||
- milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
- // process the value immediately if screen has just turned on
- mHandler.removeCallbacks(mAutoBrightnessTask);
- mLightSensorPendingDecrease = false;
- mLightSensorPendingIncrease = false;
- lightSensorChangedLocked(value);
- } else {
- if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
- (value < mLightSensorValue && mLightSensorPendingIncrease) ||
- (value == mLightSensorValue) ||
- (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) {
- // delay processing to debounce the sensor
- mHandler.removeCallbacks(mAutoBrightnessTask);
- mLightSensorPendingDecrease = (value < mLightSensorValue);
- mLightSensorPendingIncrease = (value > mLightSensorValue);
- if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
- mLightSensorPendingValue = value;
- mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
- }
- } else {
- mLightSensorPendingValue = value;
- }
- }
+ handleLightSensorValue((int)event.values[0], false);
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3ae62ad..762acbb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -133,8 +133,8 @@ class ServerThread extends Thread {
// Critical services...
try {
- Slog.i(TAG, "Entropy Service");
- ServiceManager.addService("entropy", new EntropyService());
+ Slog.i(TAG, "Entropy Mixer");
+ ServiceManager.addService("entropy", new EntropyMixer());
Slog.i(TAG, "Power Manager");
power = new PowerManagerService();
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index c7fbc00..c5c2901 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -65,7 +65,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
// 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 boolean ENABLE_LAUNCH_DESK_DOCK_APP = false;
private static final int MSG_UPDATE_TWILIGHT = 0;
private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
@@ -90,6 +90,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
private int mNightMode = UiModeManager.MODE_NIGHT_NO;
private boolean mCarModeEnabled = false;
private boolean mCharging = false;
+ private final int mDefaultUiModeType;
private final boolean mCarModeKeepsScreenOn;
private final boolean mDeskModeKeepsScreenOn;
@@ -347,6 +348,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
mConfiguration.setToDefaults();
+ mDefaultUiModeType = context.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultUiModeType);
mCarModeKeepsScreenOn = (context.getResources().getInteger(
com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
mDeskModeKeepsScreenOn = (context.getResources().getInteger(
@@ -452,7 +455,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
}
final void updateConfigurationLocked(boolean sendIt) {
- int uiMode = Configuration.UI_MODE_TYPE_NORMAL;
+ int uiMode = mDefaultUiModeType;
if (mCarModeEnabled) {
uiMode = Configuration.UI_MODE_TYPE_CAR;
} else if (isDeskDockState(mDockState)) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index aef3426..5208785 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -61,6 +61,7 @@ import android.text.TextUtils;
import android.util.Slog;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@@ -112,6 +113,10 @@ public class WifiService extends IWifiManager.Stub {
private int mScanLocksAcquired;
private int mScanLocksReleased;
+ /* A mapping from UID to scan count */
+ private HashMap<Integer, Integer> mScanCount =
+ new HashMap<Integer, Integer>();
+
private final List<Multicaster> mMulticasters =
new ArrayList<Multicaster>();
private int mMulticastEnabled;
@@ -527,6 +532,15 @@ public class WifiService extends IWifiManager.Stub {
*/
public void startScan(boolean forceActive) {
enforceChangePermission();
+
+ int uid = Binder.getCallingUid();
+ int count = 0;
+ synchronized (mScanCount) {
+ if (mScanCount.containsKey(uid)) {
+ count = mScanCount.get(uid);
+ }
+ mScanCount.put(uid, ++count);
+ }
mWifiStateMachine.startScan(forceActive);
}
@@ -676,7 +690,12 @@ public class WifiService extends IWifiManager.Stub {
*/
public List<WifiConfiguration> getConfiguredNetworks() {
enforceAccessPermission();
- return mWifiStateMachine.syncGetConfiguredNetworks();
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.syncGetConfiguredNetworks(mWifiStateMachineChannel);
+ } else {
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+ return null;
+ }
}
/**
@@ -985,6 +1004,13 @@ public class WifiService extends IWifiManager.Stub {
}
mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
}
+
+ //Start scan stats tracking when device unplugged
+ if (pluggedType == 0) {
+ synchronized (mScanCount) {
+ mScanCount.clear();
+ }
+ }
mPluggedType = pluggedType;
} else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
@@ -1175,6 +1201,13 @@ public class WifiService extends IWifiManager.Stub {
pw.println("Locks held:");
mLocks.dump(pw);
+ pw.println("Scan count since last plugged in");
+ synchronized (mScanCount) {
+ for(int sc : mScanCount.keySet()) {
+ pw.println("UID: " + sc + " Scan count: " + mScanCount.get(sc));
+ }
+ }
+
pw.println();
pw.println("WifiWatchdogStateMachine dump");
mWifiWatchdogStateMachine.dump(pw);
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index b70ed96..8bda755 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -46,7 +46,6 @@ import android.text.TextUtils.SimpleStringSplitter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IWindow;
-import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -96,6 +95,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private static final int DO_SET_SERVICE_INFO = 10;
+ public static final int ACTIVE_WINDOW_ID = -1;
+
+ public static final long ROOT_NODE_ID = -1;
+
private static int sNextWindowId;
final HandlerCaller mCaller;
@@ -467,7 +470,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- public void registerEventListener(IEventListener listener) {
+ public void registerUiTestAutomationService(IEventListener listener,
+ AccessibilityServiceInfo accessibilityServiceInfo) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
FUNCTION_REGISTER_EVENT_LISTENER);
ComponentName componentName = new ComponentName("foo.bar",
@@ -490,13 +494,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
// Hook the automation service up.
- AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
- accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
- accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
Service service = new Service(componentName, accessibilityServiceInfo, true);
service.onServiceConnected(componentName, listener.asBinder());
}
+ public void unregisterUiTestAutomationService(IEventListener listener) {
+ synchronized (mLock) {
+ final int serviceCount = mServices.size();
+ for (int i = 0; i < serviceCount; i++) {
+ Service service = mServices.get(i);
+ if (service.mServiceInterface == listener && service.mIsAutomation) {
+ // Automation service is not bound, so pretend it died to perform clean up.
+ service.binderDied();
+ }
+ }
+ }
+ }
+
/**
* Removes an AccessibilityInteractionConnection.
*
@@ -1070,10 +1084,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- public float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId,
- int interactionId, IAccessibilityInteractionConnectionCallback callback,
- long interrogatingTid)
+ public float findAccessibilityNodeInfoByViewId(int accessibilityWindowId,
+ long accessibilityNodeId, int viewId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
+ final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
@@ -1081,12 +1096,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (!permissionGranted) {
return 0;
} else {
- connection = getConnectionToRetrievalAllowingWindowLocked();
+ connection = getConnectionLocked(resolvedWindowId);
if (connection == null) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "No interaction connection to a retrieve "
- + "allowing window.");
- }
return 0;
}
}
@@ -1094,44 +1105,33 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, callback,
- interrogatingPid, interrogatingTid);
+ connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId,
+ interactionId, callback, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
- Slog.e(LOG_TAG, "Error finding node.");
+ Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId().");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(mSecurityPolicy.getRetrievalAllowingWindowLocked());
- }
-
- public float findAccessibilityNodeInfosByViewTextInActiveWindow(
- String text, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, long threadId)
- throws RemoteException {
- return findAccessibilityNodeInfosByViewText(text,
- mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID, interactionId, callback,
- threadId);
+ return getCompatibilityScale(resolvedWindowId);
}
- public float findAccessibilityNodeInfosByViewText(String text,
- int accessibilityWindowId, int accessibilityViewId, int interactionId,
+ public float findAccessibilityNodeInfosByText(int accessibilityWindowId,
+ long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
+ final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
final boolean permissionGranted =
- mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
+ mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
return 0;
} else {
- connection = getConnectionToRetrievalAllowingWindowLocked();
+ connection = getConnectionLocked(resolvedWindowId);
if (connection == null) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "No interaction connection to focused window.");
- }
return 0;
}
}
@@ -1139,89 +1139,77 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId,
+ connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
interactionId, callback, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
- Slog.e(LOG_TAG, "Error finding node.");
+ Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(accessibilityWindowId);
+ return getCompatibilityScale(resolvedWindowId);
}
public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
- int accessibilityViewId, int interactionId,
+ long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
+ final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
final boolean permissionGranted =
- mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
+ mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
return 0;
} else {
- AccessibilityConnectionWrapper wrapper =
- mWindowIdToInteractionConnectionWrapperMap.get(accessibilityWindowId);
- if (wrapper == null) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "No interaction connection to window: "
- + accessibilityWindowId);
- }
+ connection = getConnectionLocked(resolvedWindowId);
+ if (connection == null) {
return 0;
}
- connection = wrapper.mConnection;
}
}
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
+ connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
interactionId, callback, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
- Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
- + accessibilityViewId);
+ Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- return getCompatibilityScale(accessibilityWindowId);
+ return getCompatibilityScale(resolvedWindowId);
}
public boolean performAccessibilityAction(int accessibilityWindowId,
- int accessibilityViewId, int action, int interactionId,
+ long accessibilityNodeId, int action, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
+ final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
- accessibilityWindowId, action);
+ resolvedWindowId, action);
if (!permissionGranted) {
return false;
} else {
- AccessibilityConnectionWrapper wrapper =
- mWindowIdToInteractionConnectionWrapperMap.get(accessibilityWindowId);
- if (wrapper == null) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "No interaction connection to window: "
- + accessibilityWindowId);
- }
+ connection = getConnectionLocked(resolvedWindowId);
+ if (connection == null) {
return false;
}
- connection = wrapper.mConnection;
}
}
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
+ connection.performAccessibilityAction(accessibilityNodeId, action, interactionId,
callback, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
- Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
- + accessibilityViewId);
+ Slog.e(LOG_TAG, "Error calling performAccessibilityAction()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
@@ -1265,14 +1253,26 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private IAccessibilityInteractionConnection getConnectionToRetrievalAllowingWindowLocked() {
- final int windowId = mSecurityPolicy.getRetrievalAllowingWindowLocked();
+ private IAccessibilityInteractionConnection getConnectionLocked(int windowId) {
if (DEBUG) {
Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
}
- AccessibilityConnectionWrapper wrapper =
- mWindowIdToInteractionConnectionWrapperMap.get(windowId);
- return (wrapper != null) ? wrapper.mConnection : null;
+ AccessibilityConnectionWrapper wrapper = mWindowIdToInteractionConnectionWrapperMap.get(
+ windowId);
+ if (wrapper != null && wrapper.mConnection != null) {
+ return wrapper.mConnection;
+ }
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to window: " + windowId);
+ }
+ return null;
+ }
+
+ private int resolveAccessibilityWindowId(int accessibilityWindowId) {
+ if (accessibilityWindowId == ACTIVE_WINDOW_ID) {
+ return mSecurityPolicy.mRetrievalAlowingWindowId;
+ }
+ return accessibilityWindowId;
}
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 df58e83..6fd5c07 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -75,6 +75,7 @@ import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -104,6 +105,7 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserId;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Pair;
@@ -111,6 +113,7 @@ import android.util.Slog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -135,6 +138,7 @@ import java.io.StringWriter;
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -149,7 +153,9 @@ import java.util.concurrent.atomic.AtomicLong;
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
+ private static final String USER_DATA_DIR = "/data/user/";
static final String TAG = "ActivityManager";
+ static final String TAG_MU = "ActivityManagerServiceMU";
static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG;
static final boolean DEBUG_SWITCH = localLOGV || false;
@@ -158,6 +164,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final boolean DEBUG_OOM_ADJ = localLOGV || false;
static final boolean DEBUG_TRANSITION = localLOGV || false;
static final boolean DEBUG_BROADCAST = localLOGV || false;
+ static final boolean DEBUG_BACKGROUND_BROADCAST = DEBUG_BROADCAST || false;
static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
static final boolean DEBUG_SERVICE = localLOGV || false;
static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
@@ -171,6 +178,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final boolean DEBUG_CONFIGURATION = localLOGV || false;
static final boolean DEBUG_POWER = localLOGV || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
+ static final boolean DEBUG_MU = localLOGV || false;
static final boolean VALIDATE_TOKENS = false;
static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -221,7 +229,8 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int CPU_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
// How long we allow a receiver to run before giving up on it.
- static final int BROADCAST_TIMEOUT = 10*1000;
+ static final int BROADCAST_FG_TIMEOUT = 10*1000;
+ static final int BROADCAST_BG_TIMEOUT = 60*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
@@ -276,33 +285,832 @@ public final class ActivityManagerService extends ActivityManagerNative
= new ArrayList<PendingActivityLaunch>();
/**
- * List of all active broadcasts that are to be executed immediately
- * (without waiting for another broadcast to finish). Currently this only
- * contains broadcasts to registered receivers, to avoid spinning up
- * a bunch of processes to execute IntentReceiver components.
+ * BROADCASTS
+ *
+ * We keep two broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts.
*/
- final ArrayList<BroadcastRecord> mParallelBroadcasts
- = new ArrayList<BroadcastRecord>();
+ public class BroadcastQueue {
+ static final String TAG = "BroadcastQueue";
- /**
- * List of all active broadcasts that are to be executed one at a time.
- * The object at the top of the list is the currently activity broadcasts;
- * those after it are waiting for the top to finish..
- */
- final ArrayList<BroadcastRecord> mOrderedBroadcasts
- = new ArrayList<BroadcastRecord>();
+ static final int MAX_BROADCAST_HISTORY = 25;
- /**
- * Historical data of past broadcasts, for debugging.
- */
- static final int MAX_BROADCAST_HISTORY = 25;
- final BroadcastRecord[] mBroadcastHistory
- = new BroadcastRecord[MAX_BROADCAST_HISTORY];
+ /**
+ * Recognizable moniker for this queue
+ */
+ String mQueueName;
- /**
- * Set when we current have a BROADCAST_INTENT_MSG in flight.
- */
- boolean mBroadcastsScheduled = false;
+ /**
+ * Timeout period for this queue's broadcasts
+ */
+ long mTimeoutPeriod;
+
+ /**
+ * Lists of all active broadcasts that are to be executed immediately
+ * (without waiting for another broadcast to finish). Currently this only
+ * contains broadcasts to registered receivers, to avoid spinning up
+ * a bunch of processes to execute IntentReceiver components. Background-
+ * and foreground-priority broadcasts are queued separately.
+ */
+ final ArrayList<BroadcastRecord> mParallelBroadcasts
+ = new ArrayList<BroadcastRecord>();
+ /**
+ * List of all active broadcasts that are to be executed one at a time.
+ * The object at the top of the list is the currently activity broadcasts;
+ * those after it are waiting for the top to finish. As with parallel
+ * broadcasts, separate background- and foreground-priority queues are
+ * maintained.
+ */
+ final ArrayList<BroadcastRecord> mOrderedBroadcasts
+ = new ArrayList<BroadcastRecord>();
+
+ /**
+ * Historical data of past broadcasts, for debugging.
+ */
+ final BroadcastRecord[] mBroadcastHistory
+ = new BroadcastRecord[MAX_BROADCAST_HISTORY];
+
+ /**
+ * Set when we current have a BROADCAST_INTENT_MSG in flight.
+ */
+ boolean mBroadcastsScheduled = false;
+
+ /**
+ * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
+ */
+ boolean mPendingBroadcastTimeoutMessage;
+
+ /**
+ * Intent broadcasts that we have tried to start, but are
+ * waiting for the application's process to be created. We only
+ * need one per scheduling class (instead of a list) because we always
+ * process broadcasts one at a time, so no others can be started while
+ * waiting for this one.
+ */
+ BroadcastRecord mPendingBroadcast = null;
+
+ /**
+ * The receiver index that is pending, to restart the broadcast if needed.
+ */
+ int mPendingBroadcastRecvIndex;
+
+ BroadcastQueue(String name, long timeoutPeriod) {
+ mQueueName = name;
+ mTimeoutPeriod = timeoutPeriod;
+ }
+
+ public boolean isPendingBroadcastProcessLocked(int pid) {
+ return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
+ }
+
+ public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
+ mParallelBroadcasts.add(r);
+ }
+
+ public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+ mOrderedBroadcasts.add(r);
+ }
+
+ public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) {
+ for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
+ if (r.intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "***** DROPPING PARALLEL ["
+ + mQueueName + "]: " + r.intent);
+ mParallelBroadcasts.set(i, r);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public final boolean replaceOrderedBroadcastLocked(BroadcastRecord r) {
+ for (int i=mOrderedBroadcasts.size()-1; i>0; i--) {
+ if (r.intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "***** DROPPING ORDERED ["
+ + mQueueName + "]: " + r.intent);
+ mOrderedBroadcasts.set(i, r);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
+ boolean didSomething = false;
+ final BroadcastRecord br = mPendingBroadcast;
+ if (br != null && br.curApp.pid == app.pid) {
+ try {
+ mPendingBroadcast = null;
+ processCurBroadcastLocked(br, app);
+ didSomething = true;
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception in new application when starting receiver "
+ + br.curComponent.flattenToShortString(), e);
+ logBroadcastReceiverDiscardLocked(br);
+ finishReceiverLocked(br, br.resultCode, br.resultData,
+ br.resultExtras, br.resultAbort, true);
+ scheduleBroadcastsLocked();
+ // We need to reset the state if we fails to start the receiver.
+ br.state = BroadcastRecord.IDLE;
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+ return didSomething;
+ }
+
+ public void skipPendingBroadcastLocked(int pid) {
+ final BroadcastRecord br = mPendingBroadcast;
+ if (br != null && br.curApp.pid == pid) {
+ br.state = BroadcastRecord.IDLE;
+ br.nextReceiver = mPendingBroadcastRecvIndex;
+ mPendingBroadcast = null;
+ scheduleBroadcastsLocked();
+ }
+ }
+
+ public void skipCurrentReceiverLocked(ProcessRecord app) {
+ boolean reschedule = false;
+ BroadcastRecord r = app.curReceiver;
+ if (r != null) {
+ // The current broadcast is waiting for this app's receiver
+ // to be finished. Looks like that's not going to happen, so
+ // let the broadcast continue.
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, true);
+ reschedule = true;
+ }
+
+ r = mPendingBroadcast;
+ if (r != null && r.curApp == app) {
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "[" + mQueueName + "] skip & discard pending app " + r);
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, true);
+ reschedule = true;
+ }
+ if (reschedule) {
+ scheduleBroadcastsLocked();
+ }
+ }
+
+ public void scheduleBroadcastsLocked() {
+ if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
+ + mQueueName + "]: current="
+ + mBroadcastsScheduled);
+
+ if (mBroadcastsScheduled) {
+ return;
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
+ mBroadcastsScheduled = true;
+ }
+
+ public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
+ if (mOrderedBroadcasts.size() > 0) {
+ final BroadcastRecord r = mOrderedBroadcasts.get(0);
+ if (r != null && r.receiver == receiver) {
+ return r;
+ }
+ }
+ return null;
+ }
+
+ public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
+ String resultData, Bundle resultExtras, boolean resultAbort,
+ boolean explicit) {
+ int state = r.state;
+ r.state = BroadcastRecord.IDLE;
+ if (state == BroadcastRecord.IDLE) {
+ if (explicit) {
+ Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
+ }
+ }
+ r.receiver = null;
+ r.intent.setComponent(null);
+ if (r.curApp != null) {
+ r.curApp.curReceiver = null;
+ }
+ if (r.curFilter != null) {
+ r.curFilter.receiverList.curBroadcast = null;
+ }
+ r.curFilter = null;
+ r.curApp = null;
+ r.curComponent = null;
+ r.curReceiver = null;
+ mPendingBroadcast = null;
+
+ r.resultCode = resultCode;
+ r.resultData = resultData;
+ r.resultExtras = resultExtras;
+ r.resultAbort = resultAbort;
+
+ // We will process the next receiver right now if this is finishing
+ // an app receiver (which is always asynchronous) or after we have
+ // come back from calling a receiver.
+ return state == BroadcastRecord.APP_RECEIVE
+ || state == BroadcastRecord.CALL_DONE_RECEIVE;
+ }
+
+ private final void processNextBroadcast(boolean fromMsg) {
+ synchronized(ActivityManagerService.this) {
+ BroadcastRecord r;
+
+ if (DEBUG_BROADCAST) Slog.v(TAG, "processNextBroadcast ["
+ + mQueueName + "]: "
+ + mParallelBroadcasts.size() + " broadcasts, "
+ + mOrderedBroadcasts.size() + " ordered broadcasts");
+
+ updateCpuStats();
+
+ if (fromMsg) {
+ mBroadcastsScheduled = false;
+ }
+
+ // First, deliver any non-serialized broadcasts right away.
+ while (mParallelBroadcasts.size() > 0) {
+ r = mParallelBroadcasts.remove(0);
+ r.dispatchTime = SystemClock.uptimeMillis();
+ r.dispatchClockTime = System.currentTimeMillis();
+ final int N = r.receivers.size();
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
+ + mQueueName + "] " + r);
+ for (int i=0; i<N; i++) {
+ Object target = r.receivers.get(i);
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Delivering non-ordered on [" + mQueueName + "] to registered "
+ + target + ": " + r);
+ deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
+ }
+ addBroadcastToHistoryLocked(r);
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
+ + mQueueName + "] " + r);
+ }
+
+ // Now take care of the next serialized one...
+
+ // If we are waiting for a process to come up to handle the next
+ // broadcast, then do nothing at this point. Just in case, we
+ // check that the process we're waiting for still exists.
+ if (mPendingBroadcast != null) {
+ if (DEBUG_BROADCAST_LIGHT) {
+ Slog.v(TAG, "processNextBroadcast ["
+ + mQueueName + "]: waiting for "
+ + mPendingBroadcast.curApp);
+ }
+
+ boolean isDead;
+ synchronized (mPidsSelfLocked) {
+ isDead = (mPidsSelfLocked.get(mPendingBroadcast.curApp.pid) == null);
+ }
+ if (!isDead) {
+ // It's still alive, so keep waiting
+ return;
+ } else {
+ Slog.w(TAG, "pending app ["
+ + mQueueName + "]" + mPendingBroadcast.curApp
+ + " died before responding to broadcast");
+ mPendingBroadcast.state = BroadcastRecord.IDLE;
+ mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
+ mPendingBroadcast = null;
+ }
+ }
+
+ boolean looped = false;
+
+ do {
+ if (mOrderedBroadcasts.size() == 0) {
+ // No more broadcasts pending, so all done!
+ scheduleAppGcsLocked();
+ if (looped) {
+ // If we had finished the last ordered broadcast, then
+ // make sure all processes have correct oom and sched
+ // adjustments.
+ updateOomAdjLocked();
+ }
+ return;
+ }
+ r = mOrderedBroadcasts.get(0);
+ boolean forceReceive = false;
+
+ // Ensure that even if something goes awry with the timeout
+ // detection, we catch "hung" broadcasts here, discard them,
+ // and continue to make progress.
+ //
+ // This is only done if the system is ready so that PRE_BOOT_COMPLETED
+ // receivers don't get executed with timeouts. They're intended for
+ // one time heavy lifting after system upgrades and can take
+ // significant amounts of time.
+ int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+ if (mProcessesReady && r.dispatchTime > 0) {
+ long now = SystemClock.uptimeMillis();
+ if ((numReceivers > 0) &&
+ (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
+ Slog.w(TAG, "Hung broadcast ["
+ + mQueueName + "] discarded after timeout failure:"
+ + " now=" + now
+ + " dispatchTime=" + r.dispatchTime
+ + " startTime=" + r.receiverTime
+ + " intent=" + r.intent
+ + " numReceivers=" + numReceivers
+ + " nextReceiver=" + r.nextReceiver
+ + " state=" + r.state);
+ broadcastTimeoutLocked(false); // forcibly finish this broadcast
+ forceReceive = true;
+ r.state = BroadcastRecord.IDLE;
+ }
+ }
+
+ if (r.state != BroadcastRecord.IDLE) {
+ if (DEBUG_BROADCAST) Slog.d(TAG,
+ "processNextBroadcast("
+ + mQueueName + ") called when not idle (state="
+ + r.state + ")");
+ return;
+ }
+
+ if (r.receivers == null || r.nextReceiver >= numReceivers
+ || r.resultAbort || forceReceive) {
+ // No more receivers for this broadcast! Send the final
+ // result if requested...
+ if (r.resultTo != null) {
+ try {
+ if (DEBUG_BROADCAST) {
+ int seq = r.intent.getIntExtra("seq", -1);
+ Slog.i(TAG, "Finishing broadcast ["
+ + mQueueName + "] " + r.intent.getAction()
+ + " seq=" + seq + " app=" + r.callerApp);
+ }
+ performReceiveLocked(r.callerApp, r.resultTo,
+ new Intent(r.intent), r.resultCode,
+ r.resultData, r.resultExtras, false, false);
+ // Set this to null so that the reference
+ // (local and remote) isnt kept in the mBroadcastHistory.
+ r.resultTo = null;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failure ["
+ + mQueueName + "] sending broadcast result of "
+ + r.intent, e);
+ }
+ }
+
+ if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
+ cancelBroadcastTimeoutLocked();
+
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
+ + r);
+
+ // ... and on to the next...
+ addBroadcastToHistoryLocked(r);
+ mOrderedBroadcasts.remove(0);
+ r = null;
+ looped = true;
+ continue;
+ }
+ } while (r == null);
+
+ // Get the next receiver...
+ int recIdx = r.nextReceiver++;
+
+ // Keep track of when this receiver started, and make sure there
+ // is a timeout message pending to kill it if need be.
+ r.receiverTime = SystemClock.uptimeMillis();
+ if (recIdx == 0) {
+ r.dispatchTime = r.receiverTime;
+ r.dispatchClockTime = System.currentTimeMillis();
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast ["
+ + mQueueName + "] " + r);
+ }
+ if (! mPendingBroadcastTimeoutMessage) {
+ long timeoutTime = r.receiverTime + mTimeoutPeriod;
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Submitting BROADCAST_TIMEOUT_MSG ["
+ + mQueueName + "] for " + r + " at " + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
+ }
+
+ Object nextReceiver = r.receivers.get(recIdx);
+ if (nextReceiver instanceof BroadcastFilter) {
+ // Simple case: this is a registered receiver who gets
+ // a direct call.
+ BroadcastFilter filter = (BroadcastFilter)nextReceiver;
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Delivering ordered ["
+ + mQueueName + "] to registered "
+ + filter + ": " + r);
+ deliverToRegisteredReceiverLocked(r, filter, r.ordered);
+ if (r.receiver == null || !r.ordered) {
+ // The receiver has already finished, so schedule to
+ // process the next one.
+ if (DEBUG_BROADCAST) Slog.v(TAG, "Quick finishing ["
+ + mQueueName + "]: ordered="
+ + r.ordered + " receiver=" + r.receiver);
+ r.state = BroadcastRecord.IDLE;
+ scheduleBroadcastsLocked();
+ }
+ return;
+ }
+
+ // Hard case: need to instantiate the receiver, possibly
+ // starting its application process to host it.
+
+ ResolveInfo info =
+ (ResolveInfo)nextReceiver;
+
+ boolean skip = false;
+ int perm = checkComponentPermission(info.activityInfo.permission,
+ r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
+ info.activityInfo.exported);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ if (!info.activityInfo.exported) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ")"
+ + " is not exported from uid " + info.activityInfo.applicationInfo.uid
+ + " due to receiver " + info.activityInfo.packageName
+ + "/" + info.activityInfo.name);
+ } else {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ")"
+ + " requires " + info.activityInfo.permission
+ + " due to receiver " + info.activityInfo.packageName
+ + "/" + info.activityInfo.name);
+ }
+ skip = true;
+ }
+ if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
+ r.requiredPermission != null) {
+ try {
+ perm = AppGlobals.getPackageManager().
+ checkPermission(r.requiredPermission,
+ info.activityInfo.applicationInfo.packageName);
+ } catch (RemoteException e) {
+ perm = PackageManager.PERMISSION_DENIED;
+ }
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent + " to "
+ + info.activityInfo.applicationInfo.packageName
+ + " requires " + r.requiredPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+ }
+ if (r.curApp != null && r.curApp.crashing) {
+ // If the target process is crashing, just skip it.
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Skipping deliver ordered ["
+ + mQueueName + "] " + r + " to " + r.curApp
+ + ": process crashing");
+ skip = true;
+ }
+
+ if (skip) {
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Skipping delivery of ordered ["
+ + mQueueName + "] " + r + " for whatever reason");
+ r.receiver = null;
+ r.curFilter = null;
+ r.state = BroadcastRecord.IDLE;
+ scheduleBroadcastsLocked();
+ return;
+ }
+
+ r.state = BroadcastRecord.APP_RECEIVE;
+ String targetProcess = info.activityInfo.processName;
+ r.curComponent = new ComponentName(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name);
+ if (r.callingUid != Process.SYSTEM_UID) {
+ info.activityInfo = getActivityInfoForUser(info.activityInfo, UserId
+ .getUserId(r.callingUid));
+ }
+ r.curReceiver = info.activityInfo;
+ if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+ Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ + info.activityInfo.applicationInfo.uid);
+ }
+
+ // Broadcast is being executed, its package can't be stopped.
+ try {
+ AppGlobals.getPackageManager().setPackageStoppedState(
+ r.curComponent.getPackageName(), false);
+ } catch (RemoteException e) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + r.curComponent.getPackageName() + ": " + e);
+ }
+
+ // Is this receiver's application already running?
+ ProcessRecord app = getProcessRecordLocked(targetProcess,
+ info.activityInfo.applicationInfo.uid);
+ if (app != null && app.thread != null) {
+ try {
+ app.addPackage(info.activityInfo.packageName);
+ processCurBroadcastLocked(r, app);
+ return;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when sending broadcast to "
+ + r.curComponent, e);
+ }
+
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
+
+ // Not running -- get it started, to be executed when the app comes up.
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Need to start app ["
+ + mQueueName + "] " + targetProcess + " for broadcast " + r);
+ if ((r.curApp=startProcessLocked(targetProcess,
+ info.activityInfo.applicationInfo, true,
+ r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
+ "broadcast", r.curComponent,
+ (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0))
+ == null) {
+ // Ah, this recipient is unavailable. Finish it if necessary,
+ // and mark the broadcast record as ready for the next.
+ Slog.w(TAG, "Unable to launch app "
+ + info.activityInfo.applicationInfo.packageName + "/"
+ + info.activityInfo.applicationInfo.uid + " for broadcast "
+ + r.intent + ": process is bad");
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, true);
+ scheduleBroadcastsLocked();
+ r.state = BroadcastRecord.IDLE;
+ return;
+ }
+
+ mPendingBroadcast = r;
+ mPendingBroadcastRecvIndex = recIdx;
+ }
+ }
+
+ final void setBroadcastTimeoutLocked(long timeoutTime) {
+ if (! mPendingBroadcastTimeoutMessage) {
+ Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
+ mHandler.sendMessageAtTime(msg, timeoutTime);
+ mPendingBroadcastTimeoutMessage = true;
+ }
+ }
+
+ final void cancelBroadcastTimeoutLocked() {
+ if (mPendingBroadcastTimeoutMessage) {
+ mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
+ mPendingBroadcastTimeoutMessage = false;
+ }
+ }
+
+ final void broadcastTimeoutLocked(boolean fromMsg) {
+ if (fromMsg) {
+ mPendingBroadcastTimeoutMessage = false;
+ }
+
+ if (mOrderedBroadcasts.size() == 0) {
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ BroadcastRecord r = mOrderedBroadcasts.get(0);
+ if (fromMsg) {
+ if (mDidDexOpt) {
+ // Delay timeouts until dexopt finishes.
+ mDidDexOpt = false;
+ long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
+ setBroadcastTimeoutLocked(timeoutTime);
+ return;
+ }
+ if (! mProcessesReady) {
+ // Only process broadcast timeouts if the system is ready. That way
+ // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
+ // to do heavy lifting for system up.
+ return;
+ }
+
+ long timeoutTime = r.receiverTime + mTimeoutPeriod;
+ if (timeoutTime > now) {
+ // We can observe premature timeouts because we do not cancel and reset the
+ // broadcast timeout message after each receiver finishes. Instead, we set up
+ // an initial timeout then kick it down the road a little further as needed
+ // when it expires.
+ if (DEBUG_BROADCAST) Slog.v(TAG,
+ "Premature timeout ["
+ + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+ + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
+ return;
+ }
+ }
+
+ Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+ + ", started " + (now - r.receiverTime) + "ms ago");
+ r.receiverTime = now;
+ r.anrCount++;
+
+ // Current receiver has passed its expiration date.
+ if (r.nextReceiver <= 0) {
+ Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
+ return;
+ }
+
+ ProcessRecord app = null;
+ String anrMessage = null;
+
+ Object curReceiver = r.receivers.get(r.nextReceiver-1);
+ Slog.w(TAG, "Receiver during timeout: " + curReceiver);
+ logBroadcastReceiverDiscardLocked(r);
+ if (curReceiver instanceof BroadcastFilter) {
+ BroadcastFilter bf = (BroadcastFilter)curReceiver;
+ if (bf.receiverList.pid != 0
+ && bf.receiverList.pid != MY_PID) {
+ synchronized (ActivityManagerService.this.mPidsSelfLocked) {
+ app = ActivityManagerService.this.mPidsSelfLocked.get(
+ bf.receiverList.pid);
+ }
+ }
+ } else {
+ app = r.curApp;
+ }
+
+ if (app != null) {
+ anrMessage = "Broadcast of " + r.intent.toString();
+ }
+
+ if (mPendingBroadcast == r) {
+ mPendingBroadcast = null;
+ }
+
+ // Move on to the next receiver.
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, true);
+ scheduleBroadcastsLocked();
+
+ if (anrMessage != null) {
+ // Post the ANR to the handler since we do not want to process ANRs while
+ // potentially holding our lock.
+ mHandler.post(new AppNotResponding(app, anrMessage));
+ }
+ }
+
+ private final void addBroadcastToHistoryLocked(BroadcastRecord r) {
+ if (r.callingUid < 0) {
+ // This was from a registerReceiver() call; ignore it.
+ return;
+ }
+ System.arraycopy(mBroadcastHistory, 0, mBroadcastHistory, 1,
+ MAX_BROADCAST_HISTORY-1);
+ r.finishTime = SystemClock.uptimeMillis();
+ mBroadcastHistory[0] = r;
+ }
+
+ final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
+ if (r.nextReceiver > 0) {
+ Object curReceiver = r.receivers.get(r.nextReceiver-1);
+ if (curReceiver instanceof BroadcastFilter) {
+ BroadcastFilter bf = (BroadcastFilter) curReceiver;
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
+ System.identityHashCode(r),
+ r.intent.getAction(),
+ r.nextReceiver - 1,
+ System.identityHashCode(bf));
+ } else {
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+ System.identityHashCode(r),
+ r.intent.getAction(),
+ r.nextReceiver - 1,
+ ((ResolveInfo)curReceiver).toString());
+ }
+ } else {
+ Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
+ + r);
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+ System.identityHashCode(r),
+ r.intent.getAction(),
+ r.nextReceiver,
+ "NONE");
+ }
+ }
+
+ final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+ if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
+ || mPendingBroadcast != null) {
+ 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 = false;
+ }
+ printed = true;
+ pw.println(" Active broadcasts [" + mQueueName + "]:");
+ }
+ pw.println(" Broadcast #" + i + ":");
+ br.dump(pw, " ");
+ }
+ printed = false;
+ needSep = true;
+ for (int i=mOrderedBroadcasts.size()-1; i>=0; 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 [" + mQueueName + "]:");
+ }
+ pw.println(" Ordered Broadcast #" + i + ":");
+ mOrderedBroadcasts.get(i).dump(pw, " ");
+ }
+ if (dumpPackage == null || (mPendingBroadcast != null
+ && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" Pending broadcast [" + mQueueName + "]:");
+ if (mPendingBroadcast != null) {
+ mPendingBroadcast.dump(pw, " ");
+ } else {
+ pw.println(" (null)");
+ }
+ needSep = true;
+ }
+ }
+
+ 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 [" + mQueueName + "]:");
+ printed = true;
+ }
+ if (dumpAll) {
+ pw.print(" Historical Broadcast #"); pw.print(i); pw.println(":");
+ r.dump(pw, " ");
+ } else {
+ if (i >= 50) {
+ pw.println(" ...");
+ break;
+ }
+ pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
+ }
+ }
+
+ return needSep;
+ }
+ }
+
+ final BroadcastQueue mFgBroadcastQueue = new BroadcastQueue("foreground", BROADCAST_FG_TIMEOUT);
+ final BroadcastQueue mBgBroadcastQueue = new BroadcastQueue("background", BROADCAST_BG_TIMEOUT);
+ // Convenient for easy iteration over the queues. Foreground is first
+ // so that dispatch of foreground broadcasts gets precedence.
+ final BroadcastQueue[] mBroadcastQueues = { mFgBroadcastQueue, mBgBroadcastQueue };
+
+ BroadcastQueue broadcastQueueForIntent(Intent intent) {
+ final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
+ if (DEBUG_BACKGROUND_BROADCAST) {
+ Slog.i(TAG, "Broadcast intent " + intent + " on "
+ + (isFg ? "foreground" : "background")
+ + " queue");
+ }
+ return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
+ }
+
+ BroadcastRecord broadcastRecordForReceiverLocked(IBinder receiver) {
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ BroadcastRecord r = queue.getMatchingOrderedReceiver(receiver);
+ if (r != null) {
+ return r;
+ }
+ }
+ return null;
+ }
/**
* Activity we have told the window manager to have key focus.
@@ -456,25 +1264,6 @@ public final class ActivityManagerService extends ActivityManagerNative
private final StringBuilder mStrictModeBuffer = new StringBuilder();
/**
- * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
- */
- private boolean mPendingBroadcastTimeoutMessage;
-
- /**
- * Intent broadcast that we have tried to start, but are
- * waiting for its application's process to be created. We only
- * need one (instead of a list) because we always process broadcasts
- * one at a time, so no others can be started while waiting for this
- * one.
- */
- BroadcastRecord mPendingBroadcast = null;
-
- /**
- * The receiver index that is pending, to restart the broadcast if needed.
- */
- int mPendingBroadcastRecvIndex;
-
- /**
* Keeps track of all IIntentReceivers that have been registered for
* broadcasts. Hash keys are the receiver IBinder, hash value is
* a ReceiverList.
@@ -513,17 +1302,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
new HashMap<String, ArrayList<Intent>>();
- /**
- * All currently running services.
- */
- final HashMap<ComponentName, ServiceRecord> mServices =
- new HashMap<ComponentName, ServiceRecord>();
-
- /**
- * All currently running services indexed by the Intent used to start them.
- */
- final HashMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent =
- new HashMap<Intent.FilterComparison, ServiceRecord>();
+ final ServiceMap mServiceMap = new ServiceMap();
/**
* All currently bound service connections. Keys are the IBinder of
@@ -589,6 +1368,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final HashMap<ComponentName, ContentProviderRecord> mProvidersByClass
= new HashMap<ComponentName, ContentProviderRecord>();
+ final ProviderMap mProviderMap = new ProviderMap();
+
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
@@ -619,6 +1400,7 @@ public final class ActivityManagerService extends ActivityManagerNative
uid = _uid;
}
}
+
private static ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
/**
@@ -910,11 +1692,12 @@ public final class ActivityManagerService extends ActivityManagerNative
Intent intent = new Intent("android.intent.action.ANR");
if (!mProcessesReady) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
}
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
mContext, proc, (ActivityRecord)data.get("activity"));
@@ -987,11 +1770,13 @@ public final class ActivityManagerService extends ActivityManagerNative
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG, "Received BROADCAST_INTENT_MSG");
- processNextBroadcast(true);
+ BroadcastQueue queue = (BroadcastQueue) msg.obj;
+ queue.processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
+ final BroadcastQueue queue = (BroadcastQueue) msg.obj;
synchronized (ActivityManagerService.this) {
- broadcastTimeoutLocked(true);
+ queue.broadcastTimeoutLocked(true);
}
} break;
case SERVICE_TIMEOUT_MSG: {
@@ -1299,6 +2084,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ServiceManager.addService("activity", m);
ServiceManager.addService("meminfo", new MemBinder(m));
ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
+ ServiceManager.addService("dbinfo", new DbBinder(m));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(m));
}
@@ -1453,6 +2239,26 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ static class DbBinder extends Binder {
+ ActivityManagerService mActivityManagerService;
+ DbBinder(ActivityManagerService activityManagerService) {
+ mActivityManagerService = activityManagerService;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump dbinfo from from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " without permission " + android.Manifest.permission.DUMP);
+ return;
+ }
+
+ mActivityManagerService.dumpDbInfo(fd, pw, args);
+ }
+ }
+
static class CpuBinder extends Binder {
ActivityManagerService mActivityManagerService;
CpuBinder(ActivityManagerService activityManagerService) {
@@ -1790,6 +2596,7 @@ public final class ActivityManagerService extends ActivityManagerNative
processName);
return procs != null ? procs.valueAt(0) : null;
}
+ // uid = applyUserId(uid);
ProcessRecord proc = mProcessNames.get(processName, uid);
return proc;
}
@@ -1919,6 +2726,7 @@ public final class ActivityManagerService extends ActivityManagerNative
try {
int uid = app.info.uid;
+
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
@@ -2030,7 +2838,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- boolean startHomeActivityLocked() {
+ boolean startHomeActivityLocked(int userId) {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -2053,6 +2861,8 @@ public final class ActivityManagerService extends ActivityManagerNative
aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
+ aInfo = new ActivityInfo(aInfo);
+ aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
@@ -2061,11 +2871,10 @@ public final class ActivityManagerService extends ActivityManagerNative
null, null, 0, 0, 0, false, false, null);
}
}
-
-
+
return true;
}
-
+
/**
* Starts the "new version setup screen" if appropriate.
*/
@@ -2229,10 +3038,23 @@ public final class ActivityManagerService extends ActivityManagerNative
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+ int userId = 0;
+ if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
+ // Requesting home, set the identity to the current user
+ // HACK!
+ userId = mCurrentUserId;
+ } else {
+ // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
+ // the current user's userId
+ if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
+ userId = 0;
+ } else {
+ userId = Binder.getOrigCallingUser();
+ }
+ }
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
- grantedUriPermissions, grantedMode, resultTo, resultWho,
- requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
- null, null);
+ grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded,
+ debug, profileFile, profileFd, autoStopProfiler, null, null, userId);
}
public final WaitResult startActivityAndWait(IApplicationThread caller,
@@ -2241,10 +3063,11 @@ public final class ActivityManagerService extends ActivityManagerNative
String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
WaitResult res = new WaitResult();
+ int userId = Binder.getOrigCallingUser();
mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
- res, null);
+ res, null, userId);
return res;
}
@@ -2253,9 +3076,11 @@ public final class ActivityManagerService extends ActivityManagerNative
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
+ int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
- requestCode, onlyIfNeeded, debug, null, null, false, null, config);
+ requestCode, onlyIfNeeded,
+ debug, null, null, false, null, config, Binder.getOrigCallingUser());
+ return ret;
}
public int startActivityIntentSender(IApplicationThread caller,
@@ -2283,9 +3108,9 @@ public final class ActivityManagerService extends ActivityManagerNative
mAppSwitchesAllowedTime = 0;
}
}
-
- return pir.sendInner(0, fillInIntent, resolvedType, null,
+ int ret = pir.sendInner(0, fillInIntent, resolvedType, null,
null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
+ return ret;
}
public boolean startNextMatchingActivity(IBinder callingActivity,
@@ -2388,20 +3213,24 @@ public final class ActivityManagerService extends ActivityManagerNative
// This is so super not safe, that only the system (or okay root)
// can do it.
+ int userId = Binder.getOrigCallingUser();
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.myUid()) {
throw new SecurityException(
"startActivityInPackage only available to the system");
}
- return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+ int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false,
- null, null, false, null, null);
+ null, null, false, null, null, userId);
+ return ret;
}
public final int startActivities(IApplicationThread caller,
Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
- return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+ int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
+ Binder.getOrigCallingUser());
+ return ret;
}
public final int startActivitiesInPackage(int uid,
@@ -2414,8 +3243,9 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(
"startActivityInPackage only available to the system");
}
-
- return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
+ int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
+ UserId.getUserId(uid));
+ return ret;
}
final void addRecentTaskLocked(TaskRecord task) {
@@ -2427,8 +3257,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Remove any existing entries that are the same kind of task.
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
- if ((task.affinity != null && task.affinity.equals(tr.affinity))
- || (task.intent != null && task.intent.filterEquals(tr.intent))) {
+ if (task.userId == tr.userId
+ && ((task.affinity != null && task.affinity.equals(tr.affinity))
+ || (task.intent != null && task.intent.filterEquals(tr.intent)))) {
mRecentTasks.remove(i);
i--;
N--;
@@ -2787,7 +3618,6 @@ public final class ActivityManagerService extends ActivityManagerNative
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
IBinder threadBinder = thread.asBinder();
-
// Find the application record.
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
@@ -3164,7 +3994,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
public boolean clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer) {
+ final IPackageDataObserver observer, final int userId) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
@@ -3199,7 +4029,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Uri.fromParts("package", packageName, null));
intent.putExtra(Intent.EXTRA_UID, pkgUid);
broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
- null, null, 0, null, null, null, false, false);
+ null, null, 0, null, null, null, false, false, userId);
} catch (RemoteException e) {
}
} finally {
@@ -3294,7 +4124,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
+ final int userId = Binder.getOrigCallingUser();
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -3302,6 +4132,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
try {
pkgUid = pm.getPackageUid(packageName);
+ // Convert the uid to the one for the calling user
+ pkgUid = UserId.getUid(userId, pkgUid);
} catch (RemoteException e) {
}
if (pkgUid == -1) {
@@ -3384,7 +4216,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
broadcastIntentLocked(null, null, intent, null,
- null, 0, null, null, null, false, false, -1, uid);
+ null, 0, null, null, null, false, false, -1, uid, 0 /* TODO: Verify */);
}
Binder.restoreCallingIdentity(origId);
}
@@ -3444,7 +4276,8 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.putExtra(Intent.EXTRA_UID, uid);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false,
+ MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid));
}
private final boolean killPackageProcessesLocked(String packageName, int uid,
@@ -3548,7 +4381,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
- for (ServiceRecord service : mServices.values()) {
+ int userId = UserId.getUserId(uid);
+ for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
if (service.packageName.equals(name)
&& (service.app == null || evenPersistent || !service.app.persistent)) {
if (!doit) {
@@ -3687,12 +4521,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Can't happen; the backup manager is local
}
}
- if (mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid) {
+ if (isPendingBroadcastProcessLocked(pid)) {
Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
- mPendingBroadcast.state = BroadcastRecord.IDLE;
- mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
- mPendingBroadcast = null;
- scheduleBroadcastsLocked();
+ skipPendingBroadcastLocked(pid);
}
} else {
Slog.w(TAG, "Spurious process start timeout - pid not known for " + app);
@@ -3891,23 +4722,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- // Check if the next broadcast receiver is in this process...
- BroadcastRecord br = mPendingBroadcast;
- if (!badApp && br != null && br.curApp == app) {
+ // Check if a next-broadcast receiver is in this process...
+ if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
- mPendingBroadcast = null;
- processCurBroadcastLocked(br, app);
- didSomething = true;
+ didSomething = sendPendingBroadcastsLocked(app);
} catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting receiver "
- + br.curComponent.flattenToShortString(), e);
+ // If the app died trying to launch the receiver we declare it 'bad'
badApp = true;
- logBroadcastReceiverDiscardLocked(br);
- finishReceiverLocked(br.receiver, br.resultCode, br.resultData,
- br.resultExtras, br.resultAbort, true);
- scheduleBroadcastsLocked();
- // We need to reset the state if we fails to start the receiver.
- br.state = BroadcastRecord.IDLE;
}
}
@@ -4024,11 +4845,13 @@ public final class ActivityManagerService extends ActivityManagerNative
mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
+ SystemProperties.set("dev.bootcomplete", "1");
+ /* TODO: Send this to all users that are to be logged in on startup */
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_BOOT_COMPLETED, null),
null, null, 0, null, null,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false, MY_PID, Process.SYSTEM_UID, Binder.getOrigCallingUser());
}
}
}
@@ -4169,7 +4992,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
.getPackageUid(packageName);
- if (uid != Binder.getCallingUid()) {
+ if (UserId.getAppId(callingUid) != uid) {
String msg = "Permission Denial: getIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
@@ -4180,7 +5003,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- return getIntentSenderLocked(type, packageName, callingUid,
+ if (DEBUG_MU)
+ Slog.i(TAG_MU, "Getting intent sender for origCallingUid="
+ + Binder.getOrigCallingUid());
+ return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
token, resultWho, requestCode, intents, resolvedTypes, flags);
} catch (RemoteException e) {
@@ -4192,6 +5018,8 @@ public final class ActivityManagerService extends ActivityManagerNative
IIntentSender getIntentSenderLocked(int type,
String packageName, int callingUid, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
ActivityRecord activity = null;
if (type == INTENT_SENDER_ACTIVITY_RESULT) {
activity = mMainStack.isInStackLocked(token);
@@ -4435,7 +5263,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// If there is a uid that owns whatever is being accessed, it has
// blanket access to it regardless of the permissions it requires.
- if (owningUid >= 0 && uid == owningUid) {
+ if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
@@ -4469,7 +5297,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkComponentPermission(permission, pid, uid, -1, true);
+ return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true);
}
/**
@@ -4479,7 +5307,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int checkCallingPermission(String permission) {
return checkPermission(permission,
Binder.getCallingPid(),
- Binder.getCallingUid());
+ UserId.getAppId(Binder.getCallingUid()));
}
/**
@@ -4593,6 +5421,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pid = tlsIdentity.pid;
}
+ uid = UserId.getAppId(uid);
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -4635,7 +5464,8 @@ public final class ActivityManagerService extends ActivityManagerNative
String name = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+ UserId.getUserId(callingUid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -4891,7 +5721,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+ UserId.getUserId(callingUid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -4985,7 +5816,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+ UserId.getUserId(r.info.uid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -5224,6 +6056,12 @@ public final class ActivityManagerService extends ActivityManagerNative
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags) {
+ final int callingUid = Binder.getCallingUid();
+ // If it's the system uid asking, then use the current user id.
+ // TODO: Make sure that there aren't any other legitimate calls from the system uid that
+ // require the entire list.
+ final int callingUserId = callingUid == Process.SYSTEM_UID
+ ? mCurrentUserId : UserId.getUserId(callingUid);
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.GET_TASKS,
"getRecentTasks()");
@@ -5236,11 +6074,14 @@ public final class ActivityManagerService extends ActivityManagerNative
maxNum < N ? maxNum : N);
for (int i=0; i<N && maxNum > 0; i++) {
TaskRecord tr = mRecentTasks.get(i);
+ // Only add calling user's recent tasks
+ if (tr.userId != callingUserId) continue;
// Return the entry if desired by the caller. We always return
// the first entry, because callers always expect this to be the
- // forground app. We may filter others if the caller has
+ // foreground app. We may filter others if the caller has
// not supplied RECENT_WITH_EXCLUDED and there is some reason
// we should exclude the entry.
+
if (i == 0
|| ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
|| (tr.intent == null)
@@ -5329,7 +6170,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Find any running services associated with this app.
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
- for (ServiceRecord sr : mServices.values()) {
+ for (ServiceRecord sr : mServiceMap.getAllServices(root.userId)) {
if (sr.packageName.equals(component.getPackageName())) {
services.add(sr);
}
@@ -5700,17 +6541,23 @@ public final class ActivityManagerService extends ActivityManagerNative
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
}
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.info.uid);
+ int userId = UserId.getUserId(app.info.uid);
if (providers != null) {
final int N = providers.size();
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
+
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- ContentProviderRecord cpr = mProvidersByClass.get(comp);
+ ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info, comp);
- mProvidersByClass.put(comp, cpr);
+ mProviderMap.putProviderByClass(comp, cpr);
}
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
app.pubProviders.put(cpi.name, cpr);
app.addPackage(cpi.applicationInfo.packageName);
ensurePackageDexOpt(cpi.applicationInfo.packageName);
@@ -5820,8 +6667,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return false;
}
- private final ContentProviderHolder getContentProviderImpl(
- IApplicationThread caller, String name) {
+ private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
+ String name) {
ContentProviderRecord cpr;
ProviderInfo cpi = null;
@@ -5838,7 +6685,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// First check if this content provider has been published...
- cpr = mProvidersByName.get(name);
+ int userId = UserId.getUserId(r != null ? r.info.uid : Binder.getCallingUid());
+ cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
if (providerRunning) {
cpi = cpr.info;
@@ -5923,6 +6771,9 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
+ cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo,
+ Binder.getOrigCallingUser());
+
String msg;
if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
throw new SecurityException(msg);
@@ -5938,7 +6789,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- cpr = mProvidersByClass.get(comp);
+ cpr = mProviderMap.getProviderByClass(comp, Binder.getOrigCallingUser());
final boolean firstClass = cpr == null;
if (firstClass) {
try {
@@ -5952,6 +6803,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ cpi.name);
return null;
}
+ ai = getAppInfoForUser(ai, Binder.getOrigCallingUser());
cpr = new ContentProviderRecord(cpi, ai, comp);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
@@ -6020,9 +6872,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
- mProvidersByClass.put(comp, cpr);
+ mProviderMap.putProviderByClass(comp, cpr);
}
- mProvidersByName.put(name, cpr);
+ mProviderMap.putProviderByName(name, cpr);
incProviderCount(r, cpr);
}
}
@@ -6041,6 +6893,10 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
try {
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
+ + cpr.launchingApp);
+ }
cpr.wait();
} catch (InterruptedException ex) {
}
@@ -6058,7 +6914,8 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
- return getContentProviderImpl(caller, name);
+ ContentProviderHolder contentProvider = getContentProviderImpl(caller, name);
+ return contentProvider;
}
private ContentProviderHolder getContentProviderExternal(String name) {
@@ -6071,7 +6928,8 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
public void removeContentProvider(IApplicationThread caller, String name) {
synchronized (this) {
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
if(cpr == null) {
// remove from mProvidersByClass
if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -6086,8 +6944,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProvidersByClass.get(comp);
- if (localCpr.proc == r) {
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
+ if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+ + r.info.processName + " from process "
+ + localCpr.appInfo.processName);
+ if (localCpr.launchingApp == r) {
//should not happen. taken care of as a local provider
Slog.w(TAG, "removeContentProvider called on local provider: "
+ cpr.info.name + " in process " + r.processName);
@@ -6102,7 +6963,8 @@ public final class ActivityManagerService extends ActivityManagerNative
private void removeContentProviderExternal(String name) {
synchronized (this) {
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+ Binder.getOrigCallingUser());
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -6111,7 +6973,8 @@ public final class ActivityManagerService extends ActivityManagerNative
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProvidersByClass.get(comp);
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
+ Binder.getOrigCallingUser());
localCpr.externals--;
if (localCpr.externals < 0) {
Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -6128,6 +6991,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
final ProcessRecord r = getRecordForAppLocked(caller);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "ProcessRecord uid = " + r.info.uid);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
@@ -6144,12 +7009,14 @@ public final class ActivityManagerService extends ActivityManagerNative
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
- mProvidersByClass.put(comp, dst);
+ mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProvidersByName.put(names[j], dst);
+ mProviderMap.putProviderByName(names[j], dst);
}
int NL = mLaunchingProviders.size();
@@ -6894,8 +7761,10 @@ public final class ActivityManagerService extends ActivityManagerNative
};
}
Slog.i(TAG, "Sending system update to: " + intent.getComponent());
+ /* TODO: Send this to all users */
broadcastIntentLocked(null, null, intent, null, finisher,
- 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
+ 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+ Process.SYSTEM_UID);
if (finisher != null) {
mWaitingUpdate = true;
}
@@ -7205,28 +8074,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
void skipCurrentReceiverLocked(ProcessRecord app) {
- boolean reschedule = false;
- BroadcastRecord r = app.curReceiver;
- if (r != null) {
- // The current broadcast is waiting for this app's receiver
- // to be finished. Looks like that's not going to happen, so
- // let the broadcast continue.
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, true);
- reschedule = true;
- }
- r = mPendingBroadcast;
- if (r != null && r.curApp == app) {
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "skip & discard pending app " + r);
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, true);
- reschedule = true;
- }
- if (reschedule) {
- scheduleBroadcastsLocked();
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ queue.skipCurrentReceiverLocked(app);
}
}
@@ -7945,6 +8794,7 @@ public final class ActivityManagerService extends ActivityManagerNative
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(" provider [COMP_SPEC]: provider client-side 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");
@@ -8027,6 +8877,23 @@ public final class ActivityManagerService extends ActivityManagerNative
dumpOomLocked(fd, pw, args, opti, true);
}
return;
+ } else if ("provider".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);
+ }
+ if (!dumpProvider(fd, pw, name, newArgs, 0, dumpAll)) {
+ pw.println("No providers match: " + name);
+ pw.println("Use -h for help.");
+ }
+ return;
} else if ("providers".equals(cmd) || "prov".equals(cmd)) {
synchronized (this) {
dumpProvidersLocked(fd, pw, args, opti, true, null);
@@ -8523,8 +9390,14 @@ public final class ActivityManagerService extends ActivityManagerNative
if ("all".equals(name)) {
synchronized (this) {
- for (ServiceRecord r1 : mServices.values()) {
- services.add(r1);
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ services.add(r1);
+ }
+ }
+ } catch (RemoteException re) {
}
}
} else {
@@ -8542,18 +9415,24 @@ public final class ActivityManagerService extends ActivityManagerNative
}
synchronized (this) {
- for (ServiceRecord r1 : mServices.values()) {
- if (componentName != null) {
- if (r1.name.equals(componentName)) {
- services.add(r1);
- }
- } else if (name != null) {
- if (r1.name.flattenToString().contains(name)) {
- services.add(r1);
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ if (componentName != null) {
+ if (r1.name.equals(componentName)) {
+ services.add(r1);
+ }
+ } else if (name != null) {
+ if (r1.name.flattenToString().contains(name)) {
+ services.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
+ services.add(r1);
+ }
}
- } else if (System.identityHashCode(r1) == objectId) {
- services.add(r1);
}
+ } catch (RemoteException re) {
}
}
}
@@ -8611,6 +9490,110 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ /**
+ * There are three ways to call this:
+ * - no provider specified: dump all the providers
+ * - a flattened component name that matched an existing provider was specified as the
+ * first arg: dump that one provider
+ * - the first arg isn't the flattened component name of an existing provider:
+ * dump all providers whose component contains the first arg as a substring
+ */
+ protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+ int opti, boolean dumpAll) {
+ ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
+
+ if ("all".equals(name)) {
+ synchronized (this) {
+ for (ContentProviderRecord r1 : mProvidersByClass.values()) {
+ providers.add(r1);
+ }
+ }
+ } else {
+ ComponentName componentName = name != null
+ ? ComponentName.unflattenFromString(name) : null;
+ int objectId = 0;
+ if (componentName == null) {
+ // Not a '/' separated full component name; maybe an object ID?
+ try {
+ objectId = Integer.parseInt(name, 16);
+ name = null;
+ componentName = null;
+ } catch (RuntimeException e) {
+ }
+ }
+
+ synchronized (this) {
+ for (ContentProviderRecord r1 : mProvidersByClass.values()) {
+ if (componentName != null) {
+ if (r1.name.equals(componentName)) {
+ providers.add(r1);
+ }
+ } else if (name != null) {
+ if (r1.name.flattenToString().contains(name)) {
+ providers.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
+ providers.add(r1);
+ }
+ }
+ }
+ }
+
+ if (providers.size() <= 0) {
+ return false;
+ }
+
+ boolean needSep = false;
+ for (int i=0; i<providers.size(); i++) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ dumpProvider("", fd, pw, providers.get(i), args, dumpAll);
+ }
+ return true;
+ }
+
+ /**
+ * Invokes IApplicationThread.dumpProvider() on the thread of the specified provider if
+ * there is a thread associated with the provider.
+ */
+ private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
+ final ContentProviderRecord r, String[] args, boolean dumpAll) {
+ String innerPrefix = prefix + " ";
+ synchronized (this) {
+ pw.print(prefix); pw.print("PROVIDER ");
+ pw.print(r);
+ pw.print(" pid=");
+ if (r.proc != null) pw.println(r.proc.pid);
+ else pw.println("(not running)");
+ if (dumpAll) {
+ r.dump(pw, innerPrefix);
+ }
+ }
+ if (r.proc != null && r.proc.thread != null) {
+ pw.println(" Client:");
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.proc.thread.dumpProvider(
+ tp.getWriteFd().getFileDescriptor(), r.provider.asBinder(), args);
+ tp.setBufferPrefix(" ");
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException ex) {
+ pw.println(" Failure while dumping the provider: " + ex);
+ } catch (RemoteException ex) {
+ pw.println(" Got a RemoteException while dumping the service");
+ }
+ }
+ }
+
static class ItemMatcher {
ArrayList<ComponentName> components;
ArrayList<String> strings;
@@ -8827,84 +9810,11 @@ public final class ActivityManagerService extends ActivityManagerNative
needSep = true;
}
}
-
- if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
- || mPendingBroadcast != null) {
- 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 + ":");
- br.dump(pw, " ");
- }
- printed = false;
- for (int i=mOrderedBroadcasts.size()-1; i>=0; 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, " ");
- }
- 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;
- }
- }
- 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, " ");
- } else {
- if (i >= 50) {
- pw.println(" ...");
- break;
- }
- pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
- }
+ for (BroadcastQueue q : mBroadcastQueues) {
+ needSep = q.dumpLocked(fd, pw, args, opti, dumpAll, dumpPackage, needSep);
}
+
needSep = true;
if (mStickyBroadcasts != null && dumpPackage == null) {
@@ -8941,7 +9851,10 @@ public final class ActivityManagerService extends ActivityManagerNative
if (dumpAll) {
pw.println();
- pw.println(" mBroadcastsScheduled=" + mBroadcastsScheduled);
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ pw.println(" mBroadcastsScheduled [" + queue.mQueueName + "]="
+ + queue.mBroadcastsScheduled);
+ }
pw.println(" mHandler:");
mHandler.dump(new PrintWriterPrinter(pw), " ");
needSep = true;
@@ -8950,6 +9863,9 @@ public final class ActivityManagerService extends ActivityManagerNative
return needSep;
}
+ /**
+ * Prints a list of ServiceRecords (dumpsys activity services)
+ */
boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
boolean needSep = false;
@@ -8958,75 +9874,87 @@ public final class ActivityManagerService extends ActivityManagerNative
matcher.build(args, opti);
pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
- if (mServices.size() > 0) {
- boolean printed = false;
- long nowReal = SystemClock.elapsedRealtime();
- Iterator<ServiceRecord> it = mServices.values().iterator();
- needSep = false;
- while (it.hasNext()) {
- ServiceRecord r = it.next();
- 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();
- }
- pw.print(" * "); pw.println(r);
- if (dumpAll) {
- r.dump(pw, " ");
- needSep = true;
- } else {
- pw.print(" app="); pw.println(r.app);
- pw.print(" created=");
- 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");
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ if (mServiceMap.getAllServices(user.id).size() > 0) {
+ boolean printed = false;
+ long nowReal = SystemClock.elapsedRealtime();
+ Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+ user.id).iterator();
+ needSep = false;
+ while (it.hasNext()) {
+ ServiceRecord r = it.next();
+ 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();
+ }
+ pw.print(" * ");
+ pw.println(r);
+ if (dumpAll) {
+ r.dump(pw, " ");
+ needSep = true;
+ } else {
+ pw.print(" app=");
+ pw.println(r.app);
+ pw.print(" created=");
+ 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:");
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.thread.dumpService(
- tp.getWriteFd().getFileDescriptor(), r, args);
- tp.setBufferPrefix(" ");
- // Short timeout, since blocking here can
- // deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
+ if (dumpClient && r.app != null && r.app.thread != null) {
+ pw.println(" Client:");
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
+ r, args);
+ tp.setBufferPrefix(" ");
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(" Failure while dumping the service: " + e);
+ } catch (RemoteException e) {
+ pw.println(" Got a RemoteException while dumping the service");
+ }
+ needSep = true;
}
- } catch (IOException e) {
- pw.println(" Failure while dumping the service: " + e);
- } catch (RemoteException e) {
- pw.println(" Got a RemoteException while dumping the service");
}
- needSep = true;
+ needSep = printed;
}
}
- needSep = printed;
+ } catch (RemoteException re) {
+
}
if (mPendingServices.size() > 0) {
@@ -9136,76 +10064,8 @@ public final class ActivityManagerService extends ActivityManagerNative
matcher.build(args, opti);
pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
- if (mProvidersByClass.size() > 0) {
- boolean printed = false;
- Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it
- = mProvidersByClass.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
- ContentProviderRecord r = e.getValue();
- ComponentName comp = e.getKey();
- String cls = comp.getClassName();
- int end = cls.lastIndexOf('.');
- if (end > 0 && end < (cls.length()-2)) {
- cls = cls.substring(end+1);
- }
- 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.println(")");
- if (dumpAll) {
- r.dump(pw, " ");
- } else {
- if (r.proc != null) {
- 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);
- }
- }
- }
- }
- }
-
- if (dumpAll) {
- if (mProvidersByName.size() > 0) {
- boolean printed = false;
- Iterator<Map.Entry<String, ContentProviderRecord>> it
- = mProvidersByName.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, ContentProviderRecord> e = it.next();
- ContentProviderRecord r = e.getValue();
- if (!matcher.match(r, r.name)) {
- continue;
- }
- 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);
- }
- }
- }
+
+ mProviderMap.dumpProvidersLocked(pw, dumpAll);
if (mLaunchingProviders.size() > 0) {
boolean printed = false;
@@ -9616,6 +10476,38 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ final void dumpDbInfo(FileDescriptor fd, PrintWriter pw, String[] args) {
+ ArrayList<ProcessRecord> procs = collectProcesses(pw, 0, args);
+ if (procs == null) {
+ return;
+ }
+
+ pw.println("Applications Database Info:");
+
+ for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = procs.get(i);
+ if (r.thread != null) {
+ pw.println("\n** Database info for pid " + r.pid + " [" + r.processName + "] **");
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.thread.dumpDbInfo(tp.getWriteFd().getFileDescriptor(), args);
+ tp.go(fd);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println("Failure while dumping the app: " + r);
+ pw.flush();
+ } catch (RemoteException e) {
+ pw.println("Got a RemoteException while dumping the app " + r);
+ pw.flush();
+ }
+ }
+ }
+ }
+
final static class MemItem {
final String label;
final String shortLabel;
@@ -10059,10 +10951,10 @@ public final class ActivityManagerService extends ActivityManagerNative
cpr.notifyAll();
}
- mProvidersByClass.remove(cpr.name);
+ mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProvidersByName.remove(names[j]);
+ mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
}
Iterator<ProcessRecord> cit = cpr.clients.iterator();
@@ -10338,8 +11230,10 @@ public final class ActivityManagerService extends ActivityManagerNative
ArrayList<ActivityManager.RunningServiceInfo> res
= new ArrayList<ActivityManager.RunningServiceInfo>();
- if (mServices.size() > 0) {
- Iterator<ServiceRecord> it = mServices.values().iterator();
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ if (mServiceMap.getAllServices(userId).size() > 0) {
+ Iterator<ServiceRecord> it
+ = mServiceMap.getAllServices(userId).iterator();
while (it.hasNext() && res.size() < maxNum) {
res.add(makeRunningServiceInfoLocked(it.next()));
}
@@ -10359,7 +11253,8 @@ public final class ActivityManagerService extends ActivityManagerNative
public PendingIntent getRunningServiceControlPanel(ComponentName name) {
synchronized (this) {
- ServiceRecord r = mServices.get(name);
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ ServiceRecord r = mServiceMap.getServiceByName(name, userId);
if (r != null) {
for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
for (int i=0; i<conn.size(); i++) {
@@ -10375,7 +11270,7 @@ public final class ActivityManagerService extends ActivityManagerNative
private final ServiceRecord findServiceLocked(ComponentName name,
IBinder token) {
- ServiceRecord r = mServices.get(name);
+ ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
return r == token ? r : null;
}
@@ -10393,11 +11288,11 @@ public final class ActivityManagerService extends ActivityManagerNative
String resolvedType) {
ServiceRecord r = null;
if (service.getComponent() != null) {
- r = mServices.get(service.getComponent());
+ r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
}
if (r == null) {
Intent.FilterComparison filter = new Intent.FilterComparison(service);
- r = mServicesByIntent.get(filter);
+ r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
}
if (r == null) {
@@ -10413,7 +11308,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
- r = mServices.get(name);
+ r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
@@ -10460,11 +11355,17 @@ public final class ActivityManagerService extends ActivityManagerNative
private ServiceLookupResult retrieveServiceLocked(Intent service,
String resolvedType, int callingPid, int callingUid) {
ServiceRecord r = null;
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType
+ + " origCallingUid=" + callingUid);
+
if (service.getComponent() != null) {
- r = mServices.get(service.getComponent());
+ r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
+ }
+ if (r == null) {
+ Intent.FilterComparison filter = new Intent.FilterComparison(service);
+ r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
}
- Intent.FilterComparison filter = new Intent.FilterComparison(service);
- r = mServicesByIntent.get(filter);
if (r == null) {
try {
ResolveInfo rInfo =
@@ -10477,12 +11378,16 @@ public final class ActivityManagerService extends ActivityManagerNative
": not found");
return null;
}
-
+ if (Binder.getOrigCallingUser() > 0) {
+ sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo,
+ Binder.getOrigCallingUser());
+ }
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
- r = mServices.get(name);
+ r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
if (r == null) {
- filter = new Intent.FilterComparison(service.cloneFilter());
+ Intent.FilterComparison filter = new Intent.FilterComparison(
+ service.cloneFilter());
ServiceRestarter res = new ServiceRestarter();
BatteryStatsImpl.Uid.Pkg.Serv ss = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
@@ -10493,8 +11398,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
r = new ServiceRecord(this, ss, name, filter, sInfo, res);
res.setService(r);
- mServices.put(name, r);
- mServicesByIntent.put(filter, r);
+ mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r);
+ mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r);
// Make sure this component isn't in the pending list.
int N = mPendingServices.size();
@@ -10641,7 +11546,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app.thread == null) {
throw new RemoteException();
}
-
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ + ", ProcessRecord.uid = " + app.info.uid);
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
@@ -10838,6 +11745,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final String appName = r.processName;
ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName);
@@ -10942,8 +11851,8 @@ public final class ActivityManagerService extends ActivityManagerNative
System.identityHashCode(r), r.shortName,
(r.app != null) ? r.app.pid : -1);
- mServices.remove(r.name);
- mServicesByIntent.remove(r.intent);
+ mServiceMap.removeServiceByName(r.name, r.userId);
+ mServiceMap.removeServiceByIntent(r.intent, r.userId);
r.totalRestartCount = 0;
unscheduleServiceRestartLocked(r);
@@ -11057,6 +11966,8 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "startService: " + service + " type=" + resolvedType);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
@@ -11071,6 +11982,8 @@ public final class ActivityManagerService extends ActivityManagerNative
ComponentName startServiceInPackage(int uid,
Intent service, String resolvedType) {
synchronized(this) {
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
ComponentName res = startServiceLocked(null, service,
resolvedType, -1, uid);
@@ -11274,6 +12187,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
+ if (DEBUG_MU)
+ Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
+ + Binder.getOrigCallingUid());
final ProcessRecord callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
@@ -11316,7 +12232,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType,
- Binder.getCallingPid(), Binder.getCallingUid());
+ Binder.getCallingPid(), Binder.getOrigCallingUid());
if (res == null) {
return 0;
}
@@ -11662,7 +12578,9 @@ public final class ActivityManagerService extends ActivityManagerNative
r.callStart = false;
}
}
-
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
+ + Binder.getOrigCallingUid());
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
@@ -11893,15 +12811,25 @@ public final class ActivityManagerService extends ActivityManagerNative
return cur;
}
- private final void scheduleBroadcastsLocked() {
- if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts: current="
- + mBroadcastsScheduled);
+ boolean isPendingBroadcastProcessLocked(int pid) {
+ return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
+ || mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid);
+ }
- if (mBroadcastsScheduled) {
- return;
+ void skipPendingBroadcastLocked(int pid) {
+ Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ queue.skipPendingBroadcastLocked(pid);
+ }
+ }
+
+ // The app just attached; send any pending broadcasts that it should receive
+ boolean sendPendingBroadcastsLocked(ProcessRecord app) {
+ boolean didSomething = false;
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ didSomething |= queue.sendPendingBroadcastsLocked(app);
}
- mHandler.sendEmptyMessage(BROADCAST_INTENT_MSG);
- mBroadcastsScheduled = true;
+ return didSomething;
}
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
@@ -11983,13 +12911,12 @@ public final class ActivityManagerService extends ActivityManagerNative
int N = allSticky.size();
for (int i=0; i<N; i++) {
Intent intent = (Intent)allSticky.get(i);
- BroadcastRecord r = new BroadcastRecord(intent, null,
+ BroadcastQueue queue = broadcastQueueForIntent(intent);
+ BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, receivers, null, 0, null, null,
false, true, true);
- if (mParallelBroadcasts.size() == 0) {
- scheduleBroadcastsLocked();
- }
- mParallelBroadcasts.add(r);
+ queue.enqueueParallelBroadcastLocked(r);
+ queue.scheduleBroadcastsLocked();
}
}
@@ -12000,38 +12927,46 @@ public final class ActivityManagerService extends ActivityManagerNative
public void unregisterReceiver(IIntentReceiver receiver) {
if (DEBUG_BROADCAST) Slog.v(TAG, "Unregister receiver: " + receiver);
- boolean doNext = false;
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ boolean doTrim = false;
- synchronized(this) {
- ReceiverList rl
+ synchronized(this) {
+ ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
- if (rl != null) {
- if (rl.curBroadcast != null) {
- BroadcastRecord r = rl.curBroadcast;
- doNext = finishReceiverLocked(
- receiver.asBinder(), r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, true);
- }
+ if (rl != null) {
+ if (rl.curBroadcast != null) {
+ BroadcastRecord r = rl.curBroadcast;
+ final boolean doNext = finishReceiverLocked(
+ receiver.asBinder(), r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, true);
+ if (doNext) {
+ doTrim = true;
+ r.queue.processNextBroadcast(false);
+ }
+ }
- if (rl.app != null) {
- rl.app.receivers.remove(rl);
- }
- removeReceiverLocked(rl);
- if (rl.linkedToDeath) {
- rl.linkedToDeath = false;
- rl.receiver.asBinder().unlinkToDeath(rl, 0);
+ if (rl.app != null) {
+ rl.app.receivers.remove(rl);
+ }
+ removeReceiverLocked(rl);
+ if (rl.linkedToDeath) {
+ rl.linkedToDeath = false;
+ rl.receiver.asBinder().unlinkToDeath(rl, 0);
+ }
}
}
- }
- if (!doNext) {
- return;
+ // If we actually concluded any broadcasts, we might now be able
+ // to trim the recipients' apps from our working set
+ if (doTrim) {
+ trimApplications();
+ return;
+ }
+
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
-
- final long origId = Binder.clearCallingIdentity();
- processNextBroadcast(false);
- trimApplications();
- Binder.restoreCallingIdentity(origId);
}
void removeReceiverLocked(ReceiverList rl) {
@@ -12058,7 +12993,8 @@ public final class ActivityManagerService extends ActivityManagerNative
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle map, String requiredPermission,
- boolean ordered, boolean sticky, int callingPid, int callingUid) {
+ boolean ordered, boolean sticky, int callingPid, int callingUid,
+ int userId) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
@@ -12233,10 +13169,11 @@ public final class ActivityManagerService extends ActivityManagerNative
if (ai != null) {
receivers = new ArrayList();
ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = ai;
+ ri.activityInfo = getActivityInfoForUser(ai, userId);
receivers.add(ri);
}
} else {
+ // TODO: Apply userId
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
@@ -12261,28 +13198,17 @@ public final class ActivityManagerService extends ActivityManagerNative
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
- BroadcastRecord r = new BroadcastRecord(intent, callerApp,
+ final BroadcastQueue queue = broadcastQueueForIntent(intent);
+ BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
registeredReceivers, resultTo, resultCode, resultData, map,
ordered, sticky, false);
if (DEBUG_BROADCAST) Slog.v(
- TAG, "Enqueueing parallel broadcast " + r
- + ": prev had " + mParallelBroadcasts.size());
- boolean replaced = false;
- if (replacePending) {
- for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
- if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "***** DROPPING PARALLEL: " + intent);
- mParallelBroadcasts.set(i, r);
- replaced = true;
- break;
- }
- }
- }
+ TAG, "Enqueueing parallel broadcast " + r);
+ final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
- mParallelBroadcasts.add(r);
- scheduleBroadcastsLocked();
+ queue.enqueueParallelBroadcastLocked(r);
+ queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
@@ -12362,32 +13288,22 @@ public final class ActivityManagerService extends ActivityManagerNative
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
- BroadcastRecord r = new BroadcastRecord(intent, callerApp,
+ BroadcastQueue queue = broadcastQueueForIntent(intent);
+ BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
receivers, resultTo, resultCode, resultData, map, ordered,
sticky, false);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing ordered broadcast " + r
- + ": prev had " + mOrderedBroadcasts.size());
+ + ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) {
int seq = r.intent.getIntExtra("seq", -1);
Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
}
- boolean replaced = false;
- if (replacePending) {
- for (int i=mOrderedBroadcasts.size()-1; i>0; i--) {
- if (intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "***** DROPPING ORDERED: " + intent);
- mOrderedBroadcasts.set(i, r);
- replaced = true;
- break;
- }
- }
- }
+ boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
- mOrderedBroadcasts.add(r);
- scheduleBroadcastsLocked();
+ queue.enqueueOrderedBroadcastLocked(r);
+ queue.scheduleBroadcastsLocked();
}
}
@@ -12426,7 +13342,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
- String requiredPermission, boolean serialized, boolean sticky) {
+ String requiredPermission, boolean serialized, boolean sticky, int userId) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
@@ -12437,8 +13353,8 @@ public final class ActivityManagerService extends ActivityManagerNative
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo,
- resultCode, resultData, map, requiredPermission, serialized,
- sticky, callingPid, callingUid);
+ resultCode, resultData, map, requiredPermission, serialized, sticky,
+ callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -12447,21 +13363,21 @@ public final class ActivityManagerService extends ActivityManagerNative
int broadcastIntentInPackage(String packageName, int uid,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
- String requiredPermission, boolean serialized, boolean sticky) {
+ String requiredPermission, boolean serialized, boolean sticky, int userId) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
resultTo, resultCode, resultData, map, requiredPermission,
- serialized, sticky, -1, uid);
+ serialized, sticky, -1, uid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
- public final void unbroadcastIntent(IApplicationThread caller,
- Intent intent) {
+ // TODO: Use the userId; maybe mStickyBroadcasts need to be tied to the user.
+ public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -12494,54 +13410,14 @@ public final class ActivityManagerService extends ActivityManagerNative
private final boolean finishReceiverLocked(IBinder receiver, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort,
boolean explicit) {
- if (mOrderedBroadcasts.size() == 0) {
- if (explicit) {
- Slog.w(TAG, "finishReceiver called but no pending broadcasts");
- }
- return false;
- }
- BroadcastRecord r = mOrderedBroadcasts.get(0);
- if (r.receiver == null) {
- if (explicit) {
- Slog.w(TAG, "finishReceiver called but none active");
- }
- return false;
- }
- if (r.receiver != receiver) {
- Slog.w(TAG, "finishReceiver called but active receiver is different");
+ final BroadcastRecord r = broadcastRecordForReceiverLocked(receiver);
+ if (r == null) {
+ Slog.w(TAG, "finishReceiver called but not found on queue");
return false;
}
- int state = r.state;
- r.state = BroadcastRecord.IDLE;
- if (state == BroadcastRecord.IDLE) {
- if (explicit) {
- Slog.w(TAG, "finishReceiver called but state is IDLE");
- }
- }
- r.receiver = null;
- r.intent.setComponent(null);
- if (r.curApp != null) {
- r.curApp.curReceiver = null;
- }
- if (r.curFilter != null) {
- r.curFilter.receiverList.curBroadcast = null;
- }
- r.curFilter = null;
- r.curApp = null;
- r.curComponent = null;
- r.curReceiver = null;
- mPendingBroadcast = null;
-
- r.resultCode = resultCode;
- r.resultData = resultData;
- r.resultExtras = resultExtras;
- r.resultAbort = resultAbort;
- // We will process the next receiver right now if this is finishing
- // an app receiver (which is always asynchronous) or after we have
- // come back from calling a receiver.
- return state == BroadcastRecord.APP_RECEIVE
- || state == BroadcastRecord.CALL_DONE_RECEIVE;
+ return r.queue.finishReceiverLocked(r, resultCode, resultData, resultExtras, resultAbort,
+ explicit);
}
public void finishReceiver(IBinder who, int resultCode, String resultData,
@@ -12553,153 +13429,25 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new IllegalArgumentException("File descriptors passed in Bundle");
}
- boolean doNext;
-
final long origId = Binder.clearCallingIdentity();
+ try {
+ boolean doNext = false;
+ BroadcastRecord r = null;
- synchronized(this) {
- doNext = finishReceiverLocked(
- who, resultCode, resultData, resultExtras, resultAbort, true);
- }
-
- if (doNext) {
- processNextBroadcast(false);
- }
- trimApplications();
-
- Binder.restoreCallingIdentity(origId);
- }
-
- private final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
- if (r.nextReceiver > 0) {
- Object curReceiver = r.receivers.get(r.nextReceiver-1);
- if (curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter) curReceiver;
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
- System.identityHashCode(r),
- r.intent.getAction(),
- r.nextReceiver - 1,
- System.identityHashCode(bf));
- } else {
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
- System.identityHashCode(r),
- r.intent.getAction(),
- r.nextReceiver - 1,
- ((ResolveInfo)curReceiver).toString());
- }
- } else {
- Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
- + r);
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
- System.identityHashCode(r),
- r.intent.getAction(),
- r.nextReceiver,
- "NONE");
- }
- }
-
- private final void setBroadcastTimeoutLocked(long timeoutTime) {
- if (! mPendingBroadcastTimeoutMessage) {
- Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
- mHandler.sendMessageAtTime(msg, timeoutTime);
- mPendingBroadcastTimeoutMessage = true;
- }
- }
-
- private final void cancelBroadcastTimeoutLocked() {
- if (mPendingBroadcastTimeoutMessage) {
- mHandler.removeMessages(BROADCAST_TIMEOUT_MSG);
- mPendingBroadcastTimeoutMessage = false;
- }
- }
-
- private final void broadcastTimeoutLocked(boolean fromMsg) {
- if (fromMsg) {
- mPendingBroadcastTimeoutMessage = false;
- }
-
- if (mOrderedBroadcasts.size() == 0) {
- return;
- }
-
- long now = SystemClock.uptimeMillis();
- BroadcastRecord r = mOrderedBroadcasts.get(0);
- if (fromMsg) {
- if (mDidDexOpt) {
- // Delay timeouts until dexopt finishes.
- mDidDexOpt = false;
- long timeoutTime = SystemClock.uptimeMillis() + BROADCAST_TIMEOUT;
- setBroadcastTimeoutLocked(timeoutTime);
- return;
- }
- if (! mProcessesReady) {
- // Only process broadcast timeouts if the system is ready. That way
- // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
- // to do heavy lifting for system up.
- return;
- }
-
- long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
- if (timeoutTime > now) {
- // We can observe premature timeouts because we do not cancel and reset the
- // broadcast timeout message after each receiver finishes. Instead, we set up
- // an initial timeout then kick it down the road a little further as needed
- // when it expires.
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "Premature timeout @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
- + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
- return;
- }
- }
-
- Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
- + ", started " + (now - r.receiverTime) + "ms ago");
- r.receiverTime = now;
- r.anrCount++;
-
- // Current receiver has passed its expiration date.
- if (r.nextReceiver <= 0) {
- Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
- return;
- }
-
- ProcessRecord app = null;
- String anrMessage = null;
-
- Object curReceiver = r.receivers.get(r.nextReceiver-1);
- Slog.w(TAG, "Receiver during timeout: " + curReceiver);
- logBroadcastReceiverDiscardLocked(r);
- if (curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter)curReceiver;
- if (bf.receiverList.pid != 0
- && bf.receiverList.pid != MY_PID) {
- synchronized (this.mPidsSelfLocked) {
- app = this.mPidsSelfLocked.get(
- bf.receiverList.pid);
+ synchronized(this) {
+ r = broadcastRecordForReceiverLocked(who);
+ if (r != null) {
+ doNext = r.queue.finishReceiverLocked(r, resultCode,
+ resultData, resultExtras, resultAbort, true);
}
}
- } else {
- app = r.curApp;
- }
-
- if (app != null) {
- anrMessage = "Broadcast of " + r.intent.toString();
- }
-
- if (mPendingBroadcast == r) {
- mPendingBroadcast = null;
- }
- // Move on to the next receiver.
- finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, true);
- scheduleBroadcastsLocked();
-
- if (anrMessage != null) {
- // Post the ANR to the handler since we do not want to process ANRs while
- // potentially holding our lock.
- mHandler.post(new AppNotResponding(app, anrMessage));
+ if (doNext) {
+ r.queue.processNextBroadcast(false);
+ }
+ trimApplications();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
}
@@ -12834,334 +13582,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- private final void addBroadcastToHistoryLocked(BroadcastRecord r) {
- if (r.callingUid < 0) {
- // This was from a registerReceiver() call; ignore it.
- return;
- }
- System.arraycopy(mBroadcastHistory, 0, mBroadcastHistory, 1,
- MAX_BROADCAST_HISTORY-1);
- r.finishTime = SystemClock.uptimeMillis();
- mBroadcastHistory[0] = r;
- }
-
- private final void processNextBroadcast(boolean fromMsg) {
- synchronized(this) {
- BroadcastRecord r;
-
- if (DEBUG_BROADCAST) Slog.v(TAG, "processNextBroadcast: "
- + mParallelBroadcasts.size() + " broadcasts, "
- + mOrderedBroadcasts.size() + " ordered broadcasts");
-
- updateCpuStats();
-
- if (fromMsg) {
- mBroadcastsScheduled = false;
- }
-
- // First, deliver any non-serialized broadcasts right away.
- while (mParallelBroadcasts.size() > 0) {
- r = mParallelBroadcasts.remove(0);
- r.dispatchTime = SystemClock.uptimeMillis();
- r.dispatchClockTime = System.currentTimeMillis();
- final int N = r.receivers.size();
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast "
- + r);
- for (int i=0; i<N; i++) {
- Object target = r.receivers.get(i);
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "Delivering non-ordered to registered "
- + target + ": " + r);
- deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
- }
- addBroadcastToHistoryLocked(r);
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast "
- + r);
- }
-
- // Now take care of the next serialized one...
-
- // If we are waiting for a process to come up to handle the next
- // broadcast, then do nothing at this point. Just in case, we
- // check that the process we're waiting for still exists.
- if (mPendingBroadcast != null) {
- if (DEBUG_BROADCAST_LIGHT) {
- Slog.v(TAG, "processNextBroadcast: waiting for "
- + mPendingBroadcast.curApp);
- }
-
- boolean isDead;
- synchronized (mPidsSelfLocked) {
- isDead = (mPidsSelfLocked.get(mPendingBroadcast.curApp.pid) == null);
- }
- if (!isDead) {
- // It's still alive, so keep waiting
- return;
- } else {
- Slog.w(TAG, "pending app " + mPendingBroadcast.curApp
- + " died before responding to broadcast");
- mPendingBroadcast.state = BroadcastRecord.IDLE;
- mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
- mPendingBroadcast = null;
- }
- }
-
- boolean looped = false;
-
- do {
- if (mOrderedBroadcasts.size() == 0) {
- // No more broadcasts pending, so all done!
- scheduleAppGcsLocked();
- if (looped) {
- // If we had finished the last ordered broadcast, then
- // make sure all processes have correct oom and sched
- // adjustments.
- updateOomAdjLocked();
- }
- return;
- }
- r = mOrderedBroadcasts.get(0);
- boolean forceReceive = false;
-
- // Ensure that even if something goes awry with the timeout
- // detection, we catch "hung" broadcasts here, discard them,
- // and continue to make progress.
- //
- // This is only done if the system is ready so that PRE_BOOT_COMPLETED
- // receivers don't get executed with timeouts. They're intended for
- // one time heavy lifting after system upgrades and can take
- // significant amounts of time.
- int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
- if (mProcessesReady && r.dispatchTime > 0) {
- long now = SystemClock.uptimeMillis();
- if ((numReceivers > 0) &&
- (now > r.dispatchTime + (2*BROADCAST_TIMEOUT*numReceivers))) {
- Slog.w(TAG, "Hung broadcast discarded after timeout failure:"
- + " now=" + now
- + " dispatchTime=" + r.dispatchTime
- + " startTime=" + r.receiverTime
- + " intent=" + r.intent
- + " numReceivers=" + numReceivers
- + " nextReceiver=" + r.nextReceiver
- + " state=" + r.state);
- broadcastTimeoutLocked(false); // forcibly finish this broadcast
- forceReceive = true;
- r.state = BroadcastRecord.IDLE;
- }
- }
-
- if (r.state != BroadcastRecord.IDLE) {
- if (DEBUG_BROADCAST) Slog.d(TAG,
- "processNextBroadcast() called when not idle (state="
- + r.state + ")");
- return;
- }
-
- if (r.receivers == null || r.nextReceiver >= numReceivers
- || r.resultAbort || forceReceive) {
- // No more receivers for this broadcast! Send the final
- // result if requested...
- if (r.resultTo != null) {
- try {
- if (DEBUG_BROADCAST) {
- int seq = r.intent.getIntExtra("seq", -1);
- Slog.i(TAG, "Finishing broadcast " + r.intent.getAction()
- + " seq=" + seq + " app=" + r.callerApp);
- }
- performReceiveLocked(r.callerApp, r.resultTo,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false);
- // Set this to null so that the reference
- // (local and remote) isnt kept in the mBroadcastHistory.
- r.resultTo = null;
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure sending broadcast result of " + r.intent, e);
- }
- }
-
- if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
- cancelBroadcastTimeoutLocked();
-
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
- + r);
-
- // ... and on to the next...
- addBroadcastToHistoryLocked(r);
- mOrderedBroadcasts.remove(0);
- r = null;
- looped = true;
- continue;
- }
- } while (r == null);
-
- // Get the next receiver...
- int recIdx = r.nextReceiver++;
-
- // Keep track of when this receiver started, and make sure there
- // is a timeout message pending to kill it if need be.
- r.receiverTime = SystemClock.uptimeMillis();
- if (recIdx == 0) {
- r.dispatchTime = r.receiverTime;
- r.dispatchClockTime = System.currentTimeMillis();
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast "
- + r);
- }
- if (! mPendingBroadcastTimeoutMessage) {
- long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
- }
-
- Object nextReceiver = r.receivers.get(recIdx);
- if (nextReceiver instanceof BroadcastFilter) {
- // Simple case: this is a registered receiver who gets
- // a direct call.
- BroadcastFilter filter = (BroadcastFilter)nextReceiver;
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "Delivering ordered to registered "
- + filter + ": " + r);
- deliverToRegisteredReceiverLocked(r, filter, r.ordered);
- if (r.receiver == null || !r.ordered) {
- // The receiver has already finished, so schedule to
- // process the next one.
- if (DEBUG_BROADCAST) Slog.v(TAG, "Quick finishing: ordered="
- + r.ordered + " receiver=" + r.receiver);
- r.state = BroadcastRecord.IDLE;
- scheduleBroadcastsLocked();
- }
- return;
- }
-
- // Hard case: need to instantiate the receiver, possibly
- // starting its application process to host it.
-
- ResolveInfo info =
- (ResolveInfo)nextReceiver;
-
- boolean skip = false;
- int perm = checkComponentPermission(info.activityInfo.permission,
- r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
- info.activityInfo.exported);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- if (!info.activityInfo.exported) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " is not exported from uid " + info.activityInfo.applicationInfo.uid
- + " due to receiver " + info.activityInfo.packageName
- + "/" + info.activityInfo.name);
- } else {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " requires " + info.activityInfo.permission
- + " due to receiver " + info.activityInfo.packageName
- + "/" + info.activityInfo.name);
- }
- skip = true;
- }
- if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
- r.requiredPermission != null) {
- try {
- perm = AppGlobals.getPackageManager().
- checkPermission(r.requiredPermission,
- info.activityInfo.applicationInfo.packageName);
- } catch (RemoteException e) {
- perm = PackageManager.PERMISSION_DENIED;
- }
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent + " to "
- + info.activityInfo.applicationInfo.packageName
- + " requires " + r.requiredPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- }
- if (r.curApp != null && r.curApp.crashing) {
- // If the target process is crashing, just skip it.
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "Skipping deliver ordered " + r + " to " + r.curApp
- + ": process crashing");
- skip = true;
- }
-
- if (skip) {
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "Skipping delivery of ordered " + r + " for whatever reason");
- r.receiver = null;
- r.curFilter = null;
- r.state = BroadcastRecord.IDLE;
- scheduleBroadcastsLocked();
- return;
- }
-
- r.state = BroadcastRecord.APP_RECEIVE;
- String targetProcess = info.activityInfo.processName;
- r.curComponent = new ComponentName(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
- r.curReceiver = info.activityInfo;
-
- // Broadcast is being executed, its package can't be stopped.
- try {
- AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false);
- } catch (RemoteException e) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + r.curComponent.getPackageName() + ": " + e);
- }
-
- // Is this receiver's application already running?
- ProcessRecord app = getProcessRecordLocked(targetProcess,
- info.activityInfo.applicationInfo.uid);
- if (app != null && app.thread != null) {
- try {
- app.addPackage(info.activityInfo.packageName);
- processCurBroadcastLocked(r, app);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when sending broadcast to "
- + r.curComponent, e);
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- // Not running -- get it started, to be executed when the app comes up.
- if (DEBUG_BROADCAST) Slog.v(TAG,
- "Need to start app " + targetProcess + " for broadcast " + r);
- if ((r.curApp=startProcessLocked(targetProcess,
- info.activityInfo.applicationInfo, true,
- r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- "broadcast", r.curComponent,
- (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0))
- == null) {
- // Ah, this recipient is unavailable. Finish it if necessary,
- // and mark the broadcast record as ready for the next.
- Slog.w(TAG, "Unable to launch app "
- + info.activityInfo.applicationInfo.packageName + "/"
- + info.activityInfo.applicationInfo.uid + " for broadcast "
- + r.intent + ": process is bad");
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, true);
- scheduleBroadcastsLocked();
- r.state = BroadcastRecord.IDLE;
- return;
- }
-
- mPendingBroadcast = r;
- mPendingBroadcastRecvIndex = recIdx;
- }
- }
-
// =========================================================
// INSTRUMENTATION
// =========================================================
@@ -13433,12 +13853,12 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID);
+ null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_LOCALE_CHANGED),
null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID);
+ null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
}
}
}
@@ -13484,6 +13904,28 @@ public final class ActivityManagerService extends ActivityManagerNative
// LIFETIME MANAGEMENT
// =========================================================
+ // Returns which broadcast queue the app is the current [or imminent] receiver
+ // on, or 'null' if the app is not an active broadcast recipient.
+ private BroadcastQueue isReceivingBroadcast(ProcessRecord app) {
+ BroadcastRecord r = app.curReceiver;
+ if (r != null) {
+ return r.queue;
+ }
+
+ // It's not the current receiver, but it might be starting up to become one
+ synchronized (this) {
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ r = queue.mPendingBroadcast;
+ if (r != null && r.curApp == app) {
+ // found it; report which queue it's in
+ return queue;
+ }
+ }
+ }
+
+ return null;
+ }
+
private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
if (mAdjSeq == app.adjSeq) {
@@ -13549,6 +13991,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// important to least, and assign an appropriate OOM adjustment.
int adj;
int schedGroup;
+ BroadcastQueue queue;
if (app == TOP_APP) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -13560,12 +14003,14 @@ public final class ActivityManagerService extends ActivityManagerNative
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "instrumentation";
- } else if (app.curReceiver != null ||
- (mPendingBroadcast != null && mPendingBroadcast.curApp == app)) {
+ } else if ((queue = isReceivingBroadcast(app)) != null) {
// An app that is currently receiving a broadcast also
- // counts as being in the foreground.
+ // counts as being in the foreground for OOM killer purposes.
+ // It's placed in a sched group based on the nature of the
+ // broadcast as reflected by which queue it's active in.
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = (queue == mFgBroadcastQueue)
+ ? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.adjType = "broadcast";
} else if (app.executingServices.size() > 0) {
// An app that is currently executing a service callback also
@@ -14006,8 +14451,13 @@ public final class ActivityManagerService extends ActivityManagerNative
* Returns true if things are idle enough to perform GCs.
*/
private final boolean canGcNowLocked() {
- return mParallelBroadcasts.size() == 0
- && mOrderedBroadcasts.size() == 0
+ boolean processingBroadcasts = false;
+ for (BroadcastQueue q : mBroadcastQueues) {
+ if (q.mParallelBroadcasts.size() != 0 || q.mOrderedBroadcasts.size() != 0) {
+ processingBroadcasts = true;
+ }
+ }
+ return !processingBroadcasts
&& (mSleeping || (mMainStack.mResumedActivity != null &&
mMainStack.mResumedActivity.idle));
}
@@ -14751,8 +15201,157 @@ public final class ActivityManagerService extends ActivityManagerNative
// Multi-user methods
- public boolean switchUser(int userid) {
- // TODO
+ private int mCurrentUserId;
+ private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+
+ public boolean switchUser(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.myUid()) {
+ Slog.e(TAG, "Trying to switch user from unauthorized app");
+ return false;
+ }
+ if (mCurrentUserId == userId)
+ return true;
+
+ synchronized (this) {
+ // Check if user is already logged in, otherwise check if user exists first before
+ // adding to the list of logged in users.
+ if (mLoggedInUsers.indexOfKey(userId) < 0) {
+ if (!userExists(userId)) {
+ return false;
+ }
+ mLoggedInUsers.append(userId, userId);
+ }
+
+ mCurrentUserId = userId;
+ boolean haveActivities = mMainStack.switchUser(userId);
+ if (!haveActivities) {
+ startHomeActivityLocked(userId);
+ }
+ }
return true;
}
+
+ private boolean userExists(int userId) {
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ if (user.id == userId) {
+ return true;
+ }
+ }
+ } catch (RemoteException re) {
+ // Won't happen, in same process
+ }
+
+ return false;
+ }
+
+
+ private int applyUserId(int uid, int userId) {
+ return UserId.getUid(userId, uid);
+ }
+
+ private ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
+ ApplicationInfo newInfo = new ApplicationInfo(info);
+ newInfo.uid = applyUserId(info.uid, userId);
+ if (newInfo.uid >= Process.FIRST_APPLICATION_UID) {
+ newInfo.dataDir = USER_DATA_DIR + userId + "/"
+ + info.packageName;
+ }
+ return newInfo;
+ }
+
+ ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
+ if (aInfo.applicationInfo.uid < Process.FIRST_APPLICATION_UID
+ || userId < 1) {
+ return aInfo;
+ }
+
+ ActivityInfo info = new ActivityInfo(aInfo);
+ info.applicationInfo = getAppInfoForUser(info.applicationInfo, userId);
+ return info;
+ }
+
+ static class ServiceMap {
+
+ private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser
+ = new SparseArray<HashMap<ComponentName, ServiceRecord>>();
+ private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>>
+ mServicesByIntentPerUser = new SparseArray<
+ HashMap<Intent.FilterComparison, ServiceRecord>>();
+
+ ServiceRecord getServiceByName(ComponentName name, int callingUser) {
+ // TODO: Deal with global services
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser);
+ return getServices(callingUser).get(name);
+ }
+
+ ServiceRecord getServiceByName(ComponentName name) {
+ return getServiceByName(name, -1);
+ }
+
+ ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+ // TODO: Deal with global services
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser);
+ return getServicesByIntent(callingUser).get(filter);
+ }
+
+ ServiceRecord getServiceByIntent(Intent.FilterComparison filter) {
+ return getServiceByIntent(filter, -1);
+ }
+
+ void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) {
+ // TODO: Deal with global services
+ getServices(callingUser).put(name, value);
+ }
+
+ void putServiceByIntent(Intent.FilterComparison filter, int callingUser,
+ ServiceRecord value) {
+ // TODO: Deal with global services
+ getServicesByIntent(callingUser).put(filter, value);
+ }
+
+ void removeServiceByName(ComponentName name, int callingUser) {
+ // TODO: Deal with global services
+ ServiceRecord removed = getServices(callingUser).remove(name);
+ if (DEBUG_MU)
+ Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name
+ + " removed=" + removed);
+ }
+
+ void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+ // TODO: Deal with global services
+ ServiceRecord removed = getServicesByIntent(callingUser).remove(filter);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter
+ + " removed=" + removed);
+ }
+
+ Collection<ServiceRecord> getAllServices(int callingUser) {
+ // TODO: Deal with global services
+ return getServices(callingUser).values();
+ }
+
+ private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) {
+ HashMap map = mServicesByNamePerUser.get(callingUser);
+ if (map == null) {
+ map = new HashMap<ComponentName, ServiceRecord>();
+ mServicesByNamePerUser.put(callingUser, map);
+ }
+ return map;
+ }
+
+ private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent(
+ int callingUser) {
+ HashMap map = mServicesByIntentPerUser.get(callingUser);
+ if (map == null) {
+ map = new HashMap<Intent.FilterComparison, ServiceRecord>();
+ mServicesByIntentPerUser.put(callingUser, map);
+ }
+ return map;
+ }
+ }
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c819114..cdab6c6 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -25,6 +25,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Build;
@@ -34,6 +35,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -55,6 +57,7 @@ final class ActivityRecord {
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid who started the activity.
+ final int userId; // Which user is this running for?
final Intent intent; // the original intent that generated us
final ComponentName realActivity; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
@@ -124,6 +127,7 @@ final class ActivityRecord {
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
+ pw.print(prefix); pw.print("userId="); pw.print(userId);
pw.print(" app="); pw.println(app);
pw.print(prefix); pw.println(intent.toInsecureString());
pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
@@ -281,6 +285,7 @@ final class ActivityRecord {
appToken = new Token(this);
info = aInfo;
launchedFromUid = _launchedFromUid;
+ userId = UserId.getUserId(aInfo.applicationInfo.uid);
intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 6c11953..c3ae6a1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -60,6 +60,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -274,6 +275,8 @@ final class ActivityStack {
int mThumbnailWidth = -1;
int mThumbnailHeight = -1;
+ private int mCurrentUser;
+
static final int SLEEP_TIMEOUT_MSG = 8;
static final int PAUSE_TIMEOUT_MSG = 9;
static final int IDLE_TIMEOUT_MSG = 10;
@@ -365,6 +368,7 @@ final class ActivityStack {
}
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -377,6 +381,7 @@ final class ActivityStack {
}
final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -398,6 +403,7 @@ final class ActivityStack {
* @return Returns the HistoryRecord of the next activity on the stack.
*/
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -444,10 +450,11 @@ final class ActivityStack {
TaskRecord cp = null;
+ final int userId = UserId.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
- if (!r.finishing && r.task != cp
+ if (!r.finishing && r.task != cp && r.userId == userId
&& r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
cp = r.task;
//Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
@@ -487,12 +494,13 @@ final class ActivityStack {
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
+ final int userId = UserId.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
if (!r.finishing) {
- if (r.intent.getComponent().equals(cls)) {
+ if (r.intent.getComponent().equals(cls) && r.userId == userId) {
//Slog.i(TAG, "Found matching class!");
//dump();
//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
@@ -511,6 +519,43 @@ final class ActivityStack {
mService.mHandler.sendMessage(msg);
}
+ /*
+ * Move the activities around in the stack to bring a user to the foreground.
+ * @return whether there are any activities for the specified user.
+ */
+ final boolean switchUser(int userId) {
+ synchronized (mService) {
+ mCurrentUser = userId;
+
+ // Only one activity? Nothing to do...
+ if (mHistory.size() < 2)
+ return false;
+
+ boolean haveActivities = false;
+ // Check if the top activity is from the new user.
+ ActivityRecord top = mHistory.get(mHistory.size() - 1);
+ if (top.userId == userId) return true;
+ // Otherwise, move the user's activities to the top.
+ int N = mHistory.size();
+ int i = 0;
+ while (i < N) {
+ ActivityRecord r = mHistory.get(i);
+ if (r.userId == userId) {
+ ActivityRecord moveToTop = mHistory.remove(i);
+ mHistory.add(moveToTop);
+ // No need to check the top one now
+ N--;
+ haveActivities = true;
+ } else {
+ i++;
+ }
+ }
+ // Transition from the old top to the new top
+ resumeTopActivityLocked(top);
+ return haveActivities;
+ }
+ }
+
final boolean realStartActivityLocked(ActivityRecord r,
ProcessRecord app, boolean andResume, boolean checkConfig)
throws RemoteException {
@@ -1272,7 +1317,7 @@ final class ActivityStack {
// There are no more activities! Let's just start up the
// Launcher...
if (mMainStack) {
- return mService.startHomeActivityLocked();
+ return mService.startHomeActivityLocked(0);
}
}
@@ -1384,6 +1429,7 @@ final class ActivityStack {
// Launching this app's activity, make sure the app is no longer
// considered stopped.
try {
+ // TODO: Apply to the correct userId
AppGlobals.getPackageManager().setPackageStoppedState(
next.packageName, false);
} catch (RemoteException e1) {
@@ -2354,7 +2400,7 @@ final class ActivityStack {
}
}
}
-
+
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
@@ -2420,7 +2466,8 @@ final class ActivityStack {
int grantedMode, boolean onlyIfNeeded, boolean doResume) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
-
+ final int userId = r.userId;
+
int launchFlags = intent.getFlags();
// We'll invoke onUserLeaving before onPause only if the launching
@@ -2648,7 +2695,7 @@ final class ActivityStack {
// once.
ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity)) {
+ if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
if (top.app != null && top.app.thread != null) {
if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
@@ -2821,12 +2868,12 @@ final class ActivityStack {
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, String profileFile, ParcelFileDescriptor profileFd,
- boolean autoStopProfiler, WaitResult outResult, Configuration config) {
+ boolean autoStopProfiler,
+ WaitResult outResult, Configuration config, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
-
boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
@@ -2835,6 +2882,7 @@ final class ActivityStack {
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug,
profileFile, profileFd, autoStopProfiler);
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
synchronized (mService) {
int callingPid;
@@ -2915,6 +2963,7 @@ final class ActivityStack {
PackageManager.MATCH_DEFAULT_ONLY
| ActivityManagerService.STOCK_PM_FLAGS);
aInfo = rInfo != null ? rInfo.activityInfo : null;
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
} catch (RemoteException e) {
aInfo = null;
}
@@ -2977,7 +3026,8 @@ final class ActivityStack {
}
final int startActivities(IApplicationThread caller, int callingUid,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ Intent[] intents,
+ String[] resolvedTypes, IBinder resultTo, int userId) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -3022,6 +3072,8 @@ final class ActivityStack {
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false,
null, null, false);
+ // TODO: New, check if this is correct
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
& ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index bcb0134..6738e4f 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -59,6 +59,7 @@ class BroadcastRecord extends Binder {
IBinder receiver; // who is currently running, null if none.
int state;
int anrCount; // has this broadcast record hit any ANRs?
+ ActivityManagerService.BroadcastQueue queue; // the outbound queue handling this broadcast
static final int IDLE = 0;
static final int APP_RECEIVE = 1;
@@ -161,11 +162,13 @@ class BroadcastRecord extends Binder {
}
}
- BroadcastRecord(Intent _intent, ProcessRecord _callerApp, String _callerPackage,
+ BroadcastRecord(ActivityManagerService.BroadcastQueue _queue,
+ Intent _intent, ProcessRecord _callerApp, String _callerPackage,
int _callingPid, int _callingUid, String _requiredPermission,
List _receivers, IIntentReceiver _resultTo, int _resultCode,
String _resultData, Bundle _resultExtras, boolean _serialized,
boolean _sticky, boolean _initialSticky) {
+ queue = _queue;
intent = _intent;
callerApp = _callerApp;
callerPackage = _callerPackage;
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index abd2a1f..3b6a97c 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserId;
import android.util.Slog;
import java.io.PrintWriter;
@@ -248,7 +249,8 @@ class PendingIntentRecord extends IIntentSender.Stub {
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
- requiredPermission, (finishedReceiver != null), false);
+ requiredPermission, (finishedReceiver != null), false, UserId
+ .getUserId(uid));
sendFinish = false;
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
new file mode 100644
index 0000000..44e7ecc
--- /dev/null
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserId;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Keeps track of content providers by authority (name) and class. It separates the mapping by
+ * user and ones that are not user-specific (system providers).
+ */
+public class ProviderMap {
+
+ private static final String TAG = "ProviderMap";
+
+ private static final boolean DBG = false;
+
+ private final HashMap<String, ContentProviderRecord> mGlobalByName
+ = new HashMap<String, ContentProviderRecord>();
+ private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass
+ = new HashMap<ComponentName, ContentProviderRecord>();
+
+ private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
+ = new SparseArray<HashMap<String, ContentProviderRecord>>();
+ private final SparseArray<HashMap<ComponentName, ContentProviderRecord>> mProvidersByClassPerUser
+ = new SparseArray<HashMap<ComponentName, ContentProviderRecord>>();
+
+ ContentProviderRecord getProviderByName(String name) {
+ return getProviderByName(name, -1);
+ }
+
+ ContentProviderRecord getProviderByName(String name, int userId) {
+ if (DBG) {
+ Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
+ }
+ // Try to find it in the global list
+ ContentProviderRecord record = mGlobalByName.get(name);
+ if (record != null) {
+ return record;
+ }
+
+ // Check the current user's list
+ return getProvidersByName(userId).get(name);
+ }
+
+ ContentProviderRecord getProviderByClass(ComponentName name) {
+ return getProviderByClass(name, -1);
+ }
+
+ ContentProviderRecord getProviderByClass(ComponentName name, int userId) {
+ if (DBG) {
+ Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
+ }
+ // Try to find it in the global list
+ ContentProviderRecord record = mGlobalByClass.get(name);
+ if (record != null) {
+ return record;
+ }
+
+ // Check the current user's list
+ return getProvidersByClass(userId).get(name);
+ }
+
+ void putProviderByName(String name, ContentProviderRecord record) {
+ if (DBG) {
+ Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()
+ + ", record uid = " + record.appInfo.uid);
+ }
+ if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
+ mGlobalByName.put(name, record);
+ } else {
+ final int userId = UserId.getUserId(record.appInfo.uid);
+ getProvidersByName(userId).put(name, record);
+ }
+ }
+
+ void putProviderByClass(ComponentName name, ContentProviderRecord record) {
+ if (DBG) {
+ Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid()
+ + ", record uid = " + record.appInfo.uid);
+ }
+ if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
+ mGlobalByClass.put(name, record);
+ } else {
+ final int userId = UserId.getUserId(record.appInfo.uid);
+ getProvidersByClass(userId).put(name, record);
+ }
+ }
+
+ void removeProviderByName(String name, int optionalUserId) {
+ if (mGlobalByName.containsKey(name)) {
+ if (DBG)
+ Slog.i(TAG, "Removing from globalByName name=" + name);
+ mGlobalByName.remove(name);
+ } else {
+ // TODO: Verify this works, i.e., the caller happens to be from the correct user
+ if (DBG)
+ Slog.i(TAG,
+ "Removing from providersByName name=" + name + " user="
+ + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+ getProvidersByName(optionalUserId).remove(name);
+ }
+ }
+
+ void removeProviderByClass(ComponentName name, int optionalUserId) {
+ if (mGlobalByClass.containsKey(name)) {
+ if (DBG)
+ Slog.i(TAG, "Removing from globalByClass name=" + name);
+ mGlobalByClass.remove(name);
+ } else {
+ if (DBG)
+ Slog.i(TAG,
+ "Removing from providersByClass name=" + name + " user="
+ + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+ getProvidersByClass(optionalUserId).remove(name);
+ }
+ }
+
+ private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
+ final int userId = optionalUserId >= 0
+ ? optionalUserId : Binder.getOrigCallingUser();
+ final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
+ if (map == null) {
+ HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
+ mProvidersByNamePerUser.put(userId, newMap);
+ return newMap;
+ } else {
+ return map;
+ }
+ }
+
+ private HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
+ final int userId = optionalUserId >= 0
+ ? optionalUserId : Binder.getOrigCallingUser();
+ final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
+ if (map == null) {
+ HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
+ mProvidersByClassPerUser.put(userId, newMap);
+ return newMap;
+ } else {
+ return map;
+ }
+ }
+
+ private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll,
+ HashMap<ComponentName, ContentProviderRecord> map) {
+ Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
+ if (dumpAll) {
+ pw.print(" * ");
+ pw.println(r);
+ r.dump(pw, " ");
+ } else {
+ pw.print(" * ");
+ pw.print(r.name.toShortString());
+ /*
+ if (r.app != null) {
+ pw.println(":");
+ pw.print(" ");
+ pw.println(r.app);
+ } else {
+ pw.println();
+ }
+ */
+ }
+ }
+ }
+
+ private void dumpProvidersByNameLocked(PrintWriter pw,
+ HashMap<String, ContentProviderRecord> map) {
+ Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
+ pw.print(" ");
+ pw.print(e.getKey());
+ pw.print(": ");
+ pw.println(r);
+ }
+ }
+
+ void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
+ boolean needSep = false;
+ if (mGlobalByClass.size() > 0) {
+ if (needSep)
+ pw.println(" ");
+ pw.println(" Published content providers (by class):");
+ dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
+ pw.println(" ");
+ }
+
+ if (mProvidersByClassPerUser.size() > 1) {
+ for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
+ HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
+ pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":");
+ dumpProvidersByClassLocked(pw, dumpAll, map);
+ pw.println(" ");
+ }
+ } else if (mProvidersByClassPerUser.size() == 1) {
+ HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0);
+ dumpProvidersByClassLocked(pw, dumpAll, map);
+ }
+ needSep = true;
+
+ if (dumpAll) {
+ pw.println(" ");
+ pw.println(" Authority to provider mappings:");
+ dumpProvidersByNameLocked(pw, mGlobalByName);
+
+ for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
+ if (i > 0) {
+ pw.println(" User " + mProvidersByNamePerUser.keyAt(i) + ":");
+ }
+ dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 257113b..75ba947 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -25,11 +25,13 @@ import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.Slog;
import android.util.TimeUtils;
@@ -60,6 +62,7 @@ class ServiceRecord extends Binder {
// all information about the service.
final ApplicationInfo appInfo;
// information about service's app.
+ final int userId; // user that this service is running as
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String permission;// permission needed to access service
@@ -289,6 +292,7 @@ class ServiceRecord extends Binder {
this.restarter = restarter;
createTime = SystemClock.elapsedRealtime();
lastActivity = SystemClock.uptimeMillis();
+ userId = UserId.getUserId(appInfo.uid);
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index de3129b..47ec218 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,7 +19,9 @@ package com.android.server.am;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.os.UserId;
import java.io.PrintWriter;
@@ -37,6 +39,7 @@ class TaskRecord extends ThumbnailHolder {
boolean askedCompatMode;// Have asked the user about compat mode for this task.
String stringName; // caching of toString() result.
+ int userId; // user for which this task was created
TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
taskId = _taskId;
@@ -84,13 +87,17 @@ class TaskRecord extends ThumbnailHolder {
origActivity = new ComponentName(info.packageName, info.name);
}
}
-
+
if (intent != null &&
(intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
// Once we are set to an Intent with this flag, we count this
// task as having a true root activity.
rootWasReset = true;
}
+
+ if (info.applicationInfo != null) {
+ userId = UserId.getUserId(info.applicationInfo.uid);
+ }
}
void dump(PrintWriter pw, String prefix) {
@@ -154,6 +161,8 @@ class TaskRecord extends ThumbnailHolder {
} else {
sb.append(" ??");
}
+ sb.append(" U ");
+ sb.append(userId);
sb.append('}');
return stringName = sb.toString();
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index cc1df4f..9573fda 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -546,14 +546,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
ifcg = mNMService.getInterfaceConfig(iface);
if (ifcg != null) {
InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
- ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH);
+ ifcg.setLinkAddress(new LinkAddress(addr, USB_PREFIX_LENGTH));
if (enabled) {
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
+ ifcg.setInterfaceUp();
} else {
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
+ ifcg.setInterfaceDown();
}
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
+ ifcg.clearFlag("running");
mNMService.setInterfaceConfig(iface, ifcg);
}
} catch (Exception e) {
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index a76e70f..c4f9ce1 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -33,6 +33,7 @@ import android.net.INetworkManagementEventObserver;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Binder;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
@@ -47,7 +48,6 @@ import com.android.internal.net.VpnConfig;
import com.android.server.ConnectivityService.VpnCallback;
import java.io.File;
-import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charsets;
@@ -573,11 +573,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
}
// Now we are connected. Read and parse the new state.
- byte[] buffer = new byte[(int) state.length()];
- if (new FileInputStream(state).read(buffer) != buffer.length) {
- throw new IllegalStateException("Cannot read the state");
- }
- String[] parameters = new String(buffer, Charsets.UTF_8).split("\n", -1);
+ String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
if (parameters.length != 6) {
throw new IllegalStateException("Cannot parse the state");
}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 00788ba..0ce5499 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -554,8 +554,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
long delay;
- // GPS requires fresh NTP time
- if (mNtpTime.forceRefresh()) {
+ // force refresh NTP cache when outdated
+ if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
+ mNtpTime.forceRefresh();
+ }
+
+ // only update when NTP time is fresh
+ if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
long time = mNtpTime.getCachedNtpTime();
long timeReference = mNtpTime.getCachedNtpTimeReference();
long certainty = mNtpTime.getCacheCertainty();
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 8c0f1e0..9772d6a 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -48,6 +48,7 @@ import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
@@ -153,10 +154,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADDED_SNOOZE = 2;
private static final int VERSION_ADDED_RESTRICT_BACKGROUND = 3;
-
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
- private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+ private static final int VERSION_ADDED_METERED = 4;
+ private static final int VERSION_SPLIT_SNOOZE = 5;
// @VisibleForTesting
public static final int TYPE_WARNING = 0x1;
@@ -175,6 +174,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String ATTR_WARNING_BYTES = "warningBytes";
private static final String ATTR_LIMIT_BYTES = "limitBytes";
private static final String ATTR_LAST_SNOOZE = "lastSnooze";
+ private static final String ATTR_LAST_WARNING_SNOOZE = "lastWarningSnooze";
+ private static final String ATTR_LAST_LIMIT_SNOOZE = "lastLimitSnooze";
+ private static final String ATTR_METERED = "metered";
private static final String ATTR_UID = "uid";
private static final String ATTR_POLICY = "policy";
@@ -182,7 +184,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// @VisibleForTesting
public static final String ACTION_ALLOW_BACKGROUND =
- "com.android.server.action.ACTION_ALLOW_BACKGROUND";
+ "com.android.server.net.action.ALLOW_BACKGROUND";
+ public static final String ACTION_SNOOZE_WARNING =
+ "com.android.server.net.action.SNOOZE_WARNING";
private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
@@ -331,6 +335,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND);
mContext.registerReceiver(mAllowReceiver, allowFilter, MANAGE_NETWORK_POLICY, mHandler);
+ // listen for snooze warning from notifications
+ final IntentFilter snoozeWarningFilter = new IntentFilter(ACTION_SNOOZE_WARNING);
+ mContext.registerReceiver(mSnoozeWarningReceiver, snoozeWarningFilter,
+ MANAGE_NETWORK_POLICY, mHandler);
+
}
private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@@ -416,6 +425,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
};
/**
+ * Receiver that watches for {@link Notification} control of
+ * {@link NetworkPolicy#lastWarningSnooze}.
+ */
+ private BroadcastReceiver mSnoozeWarningReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and verified MANAGE_NETWORK_POLICY
+ // permission above.
+
+ final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
+ performSnooze(template, TYPE_WARNING);
+ }
+ };
+
+ /**
* Observer that watches for {@link INetworkManagementService} alerts.
*/
private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() {
@@ -456,7 +480,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long totalBytes = getTotalBytes(policy.template, start, end);
if (policy.isOverLimit(totalBytes)) {
- if (policy.lastSnooze >= start) {
+ if (policy.lastLimitSnooze >= start) {
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
enqueueNotification(policy, TYPE_LIMIT, totalBytes);
@@ -466,7 +490,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} else {
notifyUnderLimitLocked(policy.template);
- if (policy.warningBytes != WARNING_DISABLED && totalBytes >= policy.warningBytes) {
+ if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
}
}
@@ -532,7 +556,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final String tag = buildNotificationTag(policy, type);
final Notification.Builder builder = new Notification.Builder(mContext);
builder.setOnlyAlertOnce(true);
- builder.setOngoing(true);
+ builder.setWhen(0L);
final Resources res = mContext.getResources();
switch (type) {
@@ -545,9 +569,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
builder.setContentTitle(title);
builder.setContentText(body);
- final Intent intent = buildViewDataUsageIntent(policy.template);
+ final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
+ builder.setDeleteIntent(PendingIntent.getBroadcast(
+ mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
+ final Intent viewIntent = buildViewDataUsageIntent(policy.template);
builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+ mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
break;
}
case TYPE_LIMIT: {
@@ -572,6 +601,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
break;
}
+ builder.setOngoing(true);
builder.setSmallIcon(R.drawable.stat_notify_disabled);
builder.setTicker(title);
builder.setContentTitle(title);
@@ -606,6 +636,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
break;
}
+ builder.setOngoing(true);
builder.setSmallIcon(R.drawable.stat_notify_error);
builder.setTicker(title);
builder.setContentTitle(title);
@@ -718,10 +749,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long totalBytes = getTotalBytes(policy.template, start, end);
// disable data connection when over limit and not snoozed
- final boolean overLimit = policy.isOverLimit(totalBytes) && policy.lastSnooze < start;
- final boolean enabled = !overLimit;
+ final boolean overLimitWithoutSnooze = policy.isOverLimit(totalBytes)
+ && policy.lastLimitSnooze < start;
+ final boolean networkEnabled = !overLimitWithoutSnooze;
- setNetworkTemplateEnabled(policy.template, enabled);
+ setNetworkTemplateEnabled(policy.template, networkEnabled);
}
}
@@ -819,9 +851,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED;
- if (hasLimit) {
+ if (hasLimit || policy.metered) {
final long quotaBytes;
- if (policy.lastSnooze >= start) {
+ if (!hasLimit) {
+ // metered network, but no policy limit; we still need to
+ // restrict apps, so push really high quota.
+ quotaBytes = Long.MAX_VALUE;
+ } else if (policy.lastLimitSnooze >= start) {
// snoozing past quota, but we still need to restrict apps,
// so push really high quota.
quotaBytes = Long.MAX_VALUE;
@@ -890,8 +926,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int cycleDay = time.monthDay;
final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
- mNetworkPolicy.put(template, new NetworkPolicy(
- template, cycleDay, warningBytes, LIMIT_DISABLED, SNOOZE_NEVER));
+ mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay, warningBytes,
+ LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true));
writePolicyLocked();
}
}
@@ -929,17 +965,40 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY);
final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES);
final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES);
- final long lastSnooze;
- if (version >= VERSION_ADDED_SNOOZE) {
- lastSnooze = readLongAttribute(in, ATTR_LAST_SNOOZE);
+ final long lastLimitSnooze;
+ if (version >= VERSION_SPLIT_SNOOZE) {
+ lastLimitSnooze = readLongAttribute(in, ATTR_LAST_LIMIT_SNOOZE);
+ } else if (version >= VERSION_ADDED_SNOOZE) {
+ lastLimitSnooze = readLongAttribute(in, ATTR_LAST_SNOOZE);
} else {
- lastSnooze = SNOOZE_NEVER;
+ lastLimitSnooze = SNOOZE_NEVER;
+ }
+ final boolean metered;
+ if (version >= VERSION_ADDED_METERED) {
+ metered = readBooleanAttribute(in, ATTR_METERED);
+ } else {
+ switch (networkTemplate) {
+ case MATCH_MOBILE_3G_LOWER:
+ case MATCH_MOBILE_4G:
+ case MATCH_MOBILE_ALL:
+ metered = true;
+ break;
+ default:
+ metered = false;
+ }
+ }
+ final long lastWarningSnooze;
+ if (version >= VERSION_SPLIT_SNOOZE) {
+ lastWarningSnooze = readLongAttribute(in, ATTR_LAST_WARNING_SNOOZE);
+ } else {
+ lastWarningSnooze = SNOOZE_NEVER;
}
final NetworkTemplate template = new NetworkTemplate(
networkTemplate, subscriberId);
- mNetworkPolicy.put(template, new NetworkPolicy(
- template, cycleDay, warningBytes, limitBytes, lastSnooze));
+ mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay,
+ warningBytes, limitBytes, lastWarningSnooze, lastLimitSnooze,
+ metered));
} else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
@@ -994,7 +1053,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
out.startDocument(null, true);
out.startTag(null, TAG_POLICY_LIST);
- writeIntAttribute(out, ATTR_VERSION, VERSION_ADDED_RESTRICT_BACKGROUND);
+ writeIntAttribute(out, ATTR_VERSION, VERSION_SPLIT_SNOOZE);
writeBooleanAttribute(out, ATTR_RESTRICT_BACKGROUND, mRestrictBackground);
// write all known network policies
@@ -1010,7 +1069,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay);
writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes);
writeLongAttribute(out, ATTR_LIMIT_BYTES, policy.limitBytes);
- writeLongAttribute(out, ATTR_LAST_SNOOZE, policy.lastSnooze);
+ writeLongAttribute(out, ATTR_LAST_WARNING_SNOOZE, policy.lastWarningSnooze);
+ writeLongAttribute(out, ATTR_LAST_LIMIT_SNOOZE, policy.lastLimitSnooze);
+ writeBooleanAttribute(out, ATTR_METERED, policy.metered);
out.endTag(null, TAG_NETWORK_POLICY);
}
@@ -1120,9 +1181,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override
- public void snoozePolicy(NetworkTemplate template) {
+ public void snoozeLimit(NetworkTemplate template) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ performSnooze(template, TYPE_LIMIT);
+ }
+ private void performSnooze(NetworkTemplate template, int type) {
maybeRefreshTrustedTime();
final long currentTime = currentTimeMillis();
synchronized (mRulesLock) {
@@ -1132,7 +1196,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
throw new IllegalArgumentException("unable to find policy for " + template);
}
- policy.lastSnooze = currentTime;
+ switch (type) {
+ case TYPE_WARNING:
+ policy.lastWarningSnooze = currentTime;
+ break;
+ case TYPE_LIMIT:
+ policy.lastLimitSnooze = currentTime;
+ break;
+ default:
+ throw new IllegalArgumentException("unexpected type");
+ }
updateNetworkEnabledLocked();
updateNetworkRulesLocked();
@@ -1225,12 +1298,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
synchronized (mRulesLock) {
- if (argSet.contains("unsnooze")) {
+ if (argSet.contains("--unsnooze")) {
for (NetworkPolicy policy : mNetworkPolicy.values()) {
- policy.lastSnooze = SNOOZE_NEVER;
+ policy.clearSnooze();
}
+
+ updateNetworkEnabledLocked();
+ updateNetworkRulesLocked();
+ updateNotificationsLocked();
writePolicyLocked();
- fout.println("Wiped snooze timestamps");
+
+ fout.println("Cleared snooze timestamps");
return;
}
@@ -1552,6 +1630,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private long getTotalBytes(NetworkTemplate template, long start, long end) {
try {
return mNetworkStats.getSummaryForNetwork(template, start, end).getTotalBytes();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "problem reading network stats: " + e);
+ return 0;
} catch (RemoteException e) {
// ignored; service lives in system_server
return 0;
@@ -1575,6 +1656,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return new Intent(ACTION_ALLOW_BACKGROUND);
}
+ private static Intent buildSnoozeWarningIntent(NetworkTemplate template) {
+ final Intent intent = new Intent(ACTION_SNOOZE_WARNING);
+ intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+ return intent;
+ }
+
private static Intent buildNetworkOverLimitIntent(NetworkTemplate template) {
final Intent intent = new Intent();
intent.setComponent(new ComponentName(
diff --git a/services/java/com/android/server/net/NetworkStatsCollection.java b/services/java/com/android/server/net/NetworkStatsCollection.java
new file mode 100644
index 0000000..70038d9
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkStatsCollection.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.SET_ALL;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.TrafficStats.UID_REMOVED;
+
+import android.net.NetworkIdentity;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.text.format.DateUtils;
+
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FileRotator;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Objects;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import libcore.io.IoUtils;
+
+/**
+ * Collection of {@link NetworkStatsHistory}, stored based on combined key of
+ * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
+ */
+public class NetworkStatsCollection implements FileRotator.Reader {
+ private static final String TAG = "NetworkStatsCollection";
+
+ /** File header magic number: "ANET" */
+ private static final int FILE_MAGIC = 0x414E4554;
+
+ private static final int VERSION_NETWORK_INIT = 1;
+
+ private static final int VERSION_UID_INIT = 1;
+ private static final int VERSION_UID_WITH_IDENT = 2;
+ private static final int VERSION_UID_WITH_TAG = 3;
+ private static final int VERSION_UID_WITH_SET = 4;
+
+ private static final int VERSION_UNIFIED_INIT = 16;
+
+ private HashMap<Key, NetworkStatsHistory> mStats = Maps.newHashMap();
+
+ private long mBucketDuration;
+
+ private long mStartMillis;
+ private long mEndMillis;
+ private long mTotalBytes;
+ private boolean mDirty;
+
+ public NetworkStatsCollection(long bucketDuration) {
+ mBucketDuration = bucketDuration;
+ reset();
+ }
+
+ public void reset() {
+ mStats.clear();
+ mStartMillis = Long.MAX_VALUE;
+ mEndMillis = Long.MIN_VALUE;
+ mTotalBytes = 0;
+ mDirty = false;
+ }
+
+ public long getStartMillis() {
+ return mStartMillis;
+ }
+
+ public long getEndMillis() {
+ return mEndMillis;
+ }
+
+ public long getTotalBytes() {
+ return mTotalBytes;
+ }
+
+ public boolean isDirty() {
+ return mDirty;
+ }
+
+ public void clearDirty() {
+ mDirty = false;
+ }
+
+ public boolean isEmpty() {
+ return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
+ }
+
+ /**
+ * Combine all {@link NetworkStatsHistory} in this collection which match
+ * the requested parameters.
+ */
+ public NetworkStatsHistory getHistory(
+ NetworkTemplate template, int uid, int set, int tag, int fields) {
+ final NetworkStatsHistory combined = new NetworkStatsHistory(
+ mBucketDuration, estimateBuckets(), fields);
+ for (Map.Entry<Key, NetworkStatsHistory> entry : mStats.entrySet()) {
+ final Key key = entry.getKey();
+ final boolean setMatches = set == SET_ALL || key.set == set;
+ if (key.uid == uid && setMatches && key.tag == tag
+ && templateMatches(template, key.ident)) {
+ combined.recordEntireHistory(entry.getValue());
+ }
+ }
+ return combined;
+ }
+
+ /**
+ * Summarize all {@link NetworkStatsHistory} in this collection which match
+ * the requested parameters.
+ */
+ public NetworkStats getSummary(NetworkTemplate template, long start, long end) {
+ final long now = System.currentTimeMillis();
+
+ final NetworkStats stats = new NetworkStats(end - start, 24);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ NetworkStatsHistory.Entry historyEntry = null;
+
+ for (Map.Entry<Key, NetworkStatsHistory> mapEntry : mStats.entrySet()) {
+ final Key key = mapEntry.getKey();
+ if (templateMatches(template, key.ident)) {
+ final NetworkStatsHistory history = mapEntry.getValue();
+ historyEntry = history.getValues(start, end, now, historyEntry);
+
+ entry.iface = IFACE_ALL;
+ entry.uid = key.uid;
+ entry.set = key.set;
+ entry.tag = key.tag;
+ entry.rxBytes = historyEntry.rxBytes;
+ entry.rxPackets = historyEntry.rxPackets;
+ entry.txBytes = historyEntry.txBytes;
+ entry.txPackets = historyEntry.txPackets;
+ entry.operations = historyEntry.operations;
+
+ if (!entry.isEmpty()) {
+ stats.combineValues(entry);
+ }
+ }
+ }
+
+ return stats;
+ }
+
+ /**
+ * Record given {@link NetworkStats.Entry} into this collection.
+ */
+ public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
+ long end, NetworkStats.Entry entry) {
+ noteRecordedHistory(start, end, entry.rxBytes + entry.txBytes);
+ findOrCreateHistory(ident, uid, set, tag).recordData(start, end, entry);
+ }
+
+ /**
+ * Record given {@link NetworkStatsHistory} into this collection.
+ */
+ private void recordHistory(Key key, NetworkStatsHistory history) {
+ if (history.size() == 0) return;
+ noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
+
+ final NetworkStatsHistory existing = mStats.get(key);
+ if (existing != null) {
+ existing.recordEntireHistory(history);
+ } else {
+ mStats.put(key, history);
+ }
+ }
+
+ /**
+ * Record all {@link NetworkStatsHistory} contained in the given collection
+ * into this collection.
+ */
+ public void recordCollection(NetworkStatsCollection another) {
+ for (Map.Entry<Key, NetworkStatsHistory> entry : another.mStats.entrySet()) {
+ recordHistory(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private NetworkStatsHistory findOrCreateHistory(
+ NetworkIdentitySet ident, int uid, int set, int tag) {
+ final Key key = new Key(ident, uid, set, tag);
+ final NetworkStatsHistory existing = mStats.get(key);
+
+ // update when no existing, or when bucket duration changed
+ NetworkStatsHistory updated = null;
+ if (existing == null) {
+ updated = new NetworkStatsHistory(mBucketDuration, 10);
+ } else if (existing.getBucketDuration() != mBucketDuration) {
+ updated = new NetworkStatsHistory(existing, mBucketDuration);
+ }
+
+ if (updated != null) {
+ mStats.put(key, updated);
+ return updated;
+ } else {
+ return existing;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void read(InputStream in) throws IOException {
+ read(new DataInputStream(in));
+ }
+
+ public void read(DataInputStream in) throws IOException {
+ // verify file magic header intact
+ final int magic = in.readInt();
+ if (magic != FILE_MAGIC) {
+ throw new ProtocolException("unexpected magic: " + magic);
+ }
+
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_UNIFIED_INIT: {
+ // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
+ final int identSize = in.readInt();
+ for (int i = 0; i < identSize; i++) {
+ final NetworkIdentitySet ident = new NetworkIdentitySet(in);
+
+ final int size = in.readInt();
+ for (int j = 0; j < size; j++) {
+ final int uid = in.readInt();
+ final int set = in.readInt();
+ final int tag = in.readInt();
+
+ final Key key = new Key(ident, uid, set, tag);
+ final NetworkStatsHistory history = new NetworkStatsHistory(in);
+ recordHistory(key, history);
+ }
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ }
+
+ public void write(DataOutputStream out) throws IOException {
+ // cluster key lists grouped by ident
+ final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
+ for (Key key : mStats.keySet()) {
+ ArrayList<Key> keys = keysByIdent.get(key.ident);
+ if (keys == null) {
+ keys = Lists.newArrayList();
+ keysByIdent.put(key.ident, keys);
+ }
+ keys.add(key);
+ }
+
+ out.writeInt(FILE_MAGIC);
+ out.writeInt(VERSION_UNIFIED_INIT);
+
+ out.writeInt(keysByIdent.size());
+ for (NetworkIdentitySet ident : keysByIdent.keySet()) {
+ final ArrayList<Key> keys = keysByIdent.get(ident);
+ ident.writeToStream(out);
+
+ out.writeInt(keys.size());
+ for (Key key : keys) {
+ final NetworkStatsHistory history = mStats.get(key);
+ out.writeInt(key.uid);
+ out.writeInt(key.set);
+ out.writeInt(key.tag);
+ history.writeToStream(out);
+ }
+ }
+
+ out.flush();
+ }
+
+ @Deprecated
+ public void readLegacyNetwork(File file) throws IOException {
+ final AtomicFile inputFile = new AtomicFile(file);
+
+ DataInputStream in = null;
+ try {
+ in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
+
+ // verify file magic header intact
+ final int magic = in.readInt();
+ if (magic != FILE_MAGIC) {
+ throw new ProtocolException("unexpected magic: " + magic);
+ }
+
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_NETWORK_INIT: {
+ // network := size *(NetworkIdentitySet NetworkStatsHistory)
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final NetworkIdentitySet ident = new NetworkIdentitySet(in);
+ final NetworkStatsHistory history = new NetworkStatsHistory(in);
+
+ final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
+ recordHistory(key, history);
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // missing stats is okay, probably first boot
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ @Deprecated
+ public void readLegacyUid(File file, boolean onlyTags) throws IOException {
+ final AtomicFile inputFile = new AtomicFile(file);
+
+ DataInputStream in = null;
+ try {
+ in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
+
+ // verify file magic header intact
+ final int magic = in.readInt();
+ if (magic != FILE_MAGIC) {
+ throw new ProtocolException("unexpected magic: " + magic);
+ }
+
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_UID_INIT: {
+ // uid := size *(UID NetworkStatsHistory)
+
+ // drop this data version, since we don't have a good
+ // mapping into NetworkIdentitySet.
+ break;
+ }
+ case VERSION_UID_WITH_IDENT: {
+ // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
+
+ // drop this data version, since this version only existed
+ // for a short time.
+ break;
+ }
+ case VERSION_UID_WITH_TAG:
+ case VERSION_UID_WITH_SET: {
+ // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
+ final int identSize = in.readInt();
+ for (int i = 0; i < identSize; i++) {
+ final NetworkIdentitySet ident = new NetworkIdentitySet(in);
+
+ final int size = in.readInt();
+ for (int j = 0; j < size; j++) {
+ final int uid = in.readInt();
+ final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
+ : SET_DEFAULT;
+ final int tag = in.readInt();
+
+ final Key key = new Key(ident, uid, set, tag);
+ final NetworkStatsHistory history = new NetworkStatsHistory(in);
+
+ if ((tag == TAG_NONE) != onlyTags) {
+ recordHistory(key, history);
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // missing stats is okay, probably first boot
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ /**
+ * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
+ * moving any {@link NetworkStats#TAG_NONE} series to
+ * {@link TrafficStats#UID_REMOVED}.
+ */
+ public void removeUid(int uid) {
+ final ArrayList<Key> knownKeys = Lists.newArrayList();
+ knownKeys.addAll(mStats.keySet());
+
+ // migrate all UID stats into special "removed" bucket
+ for (Key key : knownKeys) {
+ if (key.uid == uid) {
+ // only migrate combined TAG_NONE history
+ if (key.tag == TAG_NONE) {
+ final NetworkStatsHistory uidHistory = mStats.get(key);
+ final NetworkStatsHistory removedHistory = findOrCreateHistory(
+ key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
+ removedHistory.recordEntireHistory(uidHistory);
+ }
+ mStats.remove(key);
+ mDirty = true;
+ }
+ }
+ }
+
+ private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
+ if (startMillis < mStartMillis) mStartMillis = startMillis;
+ if (endMillis > mEndMillis) mEndMillis = endMillis;
+ mTotalBytes += totalBytes;
+ mDirty = true;
+ }
+
+ private int estimateBuckets() {
+ return (int) (Math.min(mEndMillis - mStartMillis, DateUtils.WEEK_IN_MILLIS * 5)
+ / mBucketDuration);
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ final ArrayList<Key> keys = Lists.newArrayList();
+ keys.addAll(mStats.keySet());
+ Collections.sort(keys);
+
+ for (Key key : keys) {
+ pw.print("ident="); pw.print(key.ident.toString());
+ pw.print(" uid="); pw.print(key.uid);
+ pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
+ pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
+
+ final NetworkStatsHistory history = mStats.get(key);
+ pw.increaseIndent();
+ history.dump(pw, true);
+ pw.decreaseIndent();
+ }
+ }
+
+ /**
+ * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
+ * in the given {@link NetworkIdentitySet}.
+ */
+ private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
+ for (NetworkIdentity ident : identSet) {
+ if (template.matches(ident)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class Key implements Comparable<Key> {
+ public final NetworkIdentitySet ident;
+ public final int uid;
+ public final int set;
+ public final int tag;
+
+ private final int hashCode;
+
+ public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
+ this.ident = ident;
+ this.uid = uid;
+ this.set = set;
+ this.tag = tag;
+ hashCode = Objects.hashCode(ident, uid, set, tag);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Key) {
+ final Key key = (Key) obj;
+ return uid == key.uid && set == key.set && tag == key.tag
+ && Objects.equal(ident, key.ident);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Key another) {
+ return Integer.compare(uid, another.uid);
+ }
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java
new file mode 100644
index 0000000..e7ba358
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkStatsRecorder.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.net.NetworkStats.TAG_NONE;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.net.NetworkStats;
+import android.net.NetworkStats.NonMonotonicObserver;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.util.FileRotator;
+import com.android.internal.util.IndentingPrintWriter;
+import com.google.android.collect.Sets;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.WeakReference;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Logic to record deltas between periodic {@link NetworkStats} snapshots into
+ * {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}.
+ * Keeps pending changes in memory until they pass a specific threshold, in
+ * bytes. Uses {@link FileRotator} for persistence logic.
+ * <p>
+ * Not inherently thread safe.
+ */
+public class NetworkStatsRecorder {
+ private static final String TAG = "NetworkStatsRecorder";
+ private static final boolean LOGD = true;
+
+ private final FileRotator mRotator;
+ private final NonMonotonicObserver<String> mObserver;
+ private final String mCookie;
+
+ private final long mBucketDuration;
+ private final long mPersistThresholdBytes;
+ private final boolean mOnlyTags;
+
+ private NetworkStats mLastSnapshot;
+
+ private final NetworkStatsCollection mPending;
+ private final NetworkStatsCollection mSinceBoot;
+
+ private final CombiningRewriter mPendingRewriter;
+
+ private WeakReference<NetworkStatsCollection> mComplete;
+
+ public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
+ String cookie, long bucketDuration, long persistThresholdBytes, boolean onlyTags) {
+ mRotator = checkNotNull(rotator, "missing FileRotator");
+ mObserver = checkNotNull(observer, "missing NonMonotonicObserver");
+ mCookie = cookie;
+
+ mBucketDuration = bucketDuration;
+ mPersistThresholdBytes = persistThresholdBytes;
+ mOnlyTags = onlyTags;
+
+ mPending = new NetworkStatsCollection(bucketDuration);
+ mSinceBoot = new NetworkStatsCollection(bucketDuration);
+
+ mPendingRewriter = new CombiningRewriter(mPending);
+ }
+
+ public void resetLocked() {
+ mLastSnapshot = null;
+ mPending.reset();
+ mSinceBoot.reset();
+ mComplete.clear();
+ }
+
+ public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) {
+ return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
+ }
+
+ /**
+ * Load complete history represented by {@link FileRotator}. Caches
+ * internally as a {@link WeakReference}, and updated with future
+ * {@link #recordSnapshotLocked(NetworkStats, Map, long)} snapshots as long
+ * as reference is valid.
+ */
+ public NetworkStatsCollection getOrLoadCompleteLocked() {
+ NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
+ if (complete == null) {
+ if (LOGD) Slog.d(TAG, "getOrLoadCompleteLocked() reading from disk for " + mCookie);
+ try {
+ complete = new NetworkStatsCollection(mBucketDuration);
+ mRotator.readMatching(complete, Long.MIN_VALUE, Long.MAX_VALUE);
+ complete.recordCollection(mPending);
+ mComplete = new WeakReference<NetworkStatsCollection>(complete);
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem completely reading network stats", e);
+ }
+ }
+ return complete;
+ }
+
+ /**
+ * Record any delta that occurred since last {@link NetworkStats} snapshot,
+ * using the given {@link Map} to identify network interfaces. First
+ * snapshot is considered bootstrap, and is not counted as delta.
+ */
+ public void recordSnapshotLocked(NetworkStats snapshot,
+ Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
+ final HashSet<String> unknownIfaces = Sets.newHashSet();
+
+ // assume first snapshot is bootstrap and don't record
+ if (mLastSnapshot == null) {
+ mLastSnapshot = snapshot;
+ return;
+ }
+
+ final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
+
+ final NetworkStats delta = NetworkStats.subtract(
+ snapshot, mLastSnapshot, mObserver, mCookie);
+ final long end = currentTimeMillis;
+ final long start = end - delta.getElapsedRealtime();
+
+ NetworkStats.Entry entry = null;
+ for (int i = 0; i < delta.size(); i++) {
+ entry = delta.getValues(i, entry);
+ final NetworkIdentitySet ident = ifaceIdent.get(entry.iface);
+ if (ident == null) {
+ unknownIfaces.add(entry.iface);
+ continue;
+ }
+
+ // skip when no delta occured
+ if (entry.isEmpty()) continue;
+
+ // only record tag data when requested
+ if ((entry.tag == TAG_NONE) != mOnlyTags) {
+ mPending.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
+
+ // also record against boot stats when present
+ if (mSinceBoot != null) {
+ mSinceBoot.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
+ }
+
+ // also record against complete dataset when present
+ if (complete != null) {
+ complete.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
+ }
+ }
+ }
+
+ mLastSnapshot = snapshot;
+
+ if (LOGD && unknownIfaces.size() > 0) {
+ Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
+ }
+ }
+
+ /**
+ * Consider persisting any pending deltas, if they are beyond
+ * {@link #mPersistThresholdBytes}.
+ */
+ public void maybePersistLocked(long currentTimeMillis) {
+ final long pendingBytes = mPending.getTotalBytes();
+ if (pendingBytes >= mPersistThresholdBytes) {
+ forcePersistLocked(currentTimeMillis);
+ } else {
+ mRotator.maybeRotate(currentTimeMillis);
+ }
+ }
+
+ /**
+ * Force persisting any pending deltas.
+ */
+ public void forcePersistLocked(long currentTimeMillis) {
+ if (mPending.isDirty()) {
+ if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
+ try {
+ mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
+ mRotator.maybeRotate(currentTimeMillis);
+ mPending.reset();
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem persisting pending stats", e);
+ }
+ }
+ }
+
+ /**
+ * Remove the given UID from all {@link FileRotator} history, migrating it
+ * to {@link TrafficStats#UID_REMOVED}.
+ */
+ public void removeUidLocked(int uid) {
+ try {
+ // process all existing data to migrate uid
+ mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uid));
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem removing UID " + uid, e);
+ }
+
+ // clear UID from current stats snapshot
+ if (mLastSnapshot != null) {
+ mLastSnapshot = mLastSnapshot.withoutUid(uid);
+ }
+ }
+
+ /**
+ * Rewriter that will combine current {@link NetworkStatsCollection} values
+ * with anything read from disk, and write combined set to disk. Clears the
+ * original {@link NetworkStatsCollection} when finished writing.
+ */
+ private static class CombiningRewriter implements FileRotator.Rewriter {
+ private final NetworkStatsCollection mCollection;
+
+ public CombiningRewriter(NetworkStatsCollection collection) {
+ mCollection = checkNotNull(collection, "missing NetworkStatsCollection");
+ }
+
+ /** {@inheritDoc} */
+ public void reset() {
+ // ignored
+ }
+
+ /** {@inheritDoc} */
+ public void read(InputStream in) throws IOException {
+ mCollection.read(in);
+ }
+
+ /** {@inheritDoc} */
+ public boolean shouldWrite() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public void write(OutputStream out) throws IOException {
+ mCollection.write(new DataOutputStream(out));
+ mCollection.reset();
+ }
+ }
+
+ /**
+ * Rewriter that will remove any {@link NetworkStatsHistory} attributed to
+ * the requested UID, only writing data back when modified.
+ */
+ public static class RemoveUidRewriter implements FileRotator.Rewriter {
+ private final NetworkStatsCollection mTemp;
+ private final int mUid;
+
+ public RemoveUidRewriter(long bucketDuration, int uid) {
+ mTemp = new NetworkStatsCollection(bucketDuration);
+ mUid = uid;
+ }
+
+ /** {@inheritDoc} */
+ public void reset() {
+ mTemp.reset();
+ }
+
+ /** {@inheritDoc} */
+ public void read(InputStream in) throws IOException {
+ mTemp.read(in);
+ mTemp.clearDirty();
+ mTemp.removeUid(mUid);
+ }
+
+ /** {@inheritDoc} */
+ public boolean shouldWrite() {
+ return mTemp.isDirty();
+ }
+
+ /** {@inheritDoc} */
+ public void write(OutputStream out) throws IOException {
+ mTemp.write(new DataOutputStream(out));
+ }
+ }
+
+ public void importLegacyNetworkLocked(File file) throws IOException {
+ // legacy file still exists; start empty to avoid double importing
+ mRotator.deleteAll();
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
+ collection.readLegacyNetwork(file);
+
+ final long startMillis = collection.getStartMillis();
+ final long endMillis = collection.getEndMillis();
+
+ if (!collection.isEmpty()) {
+ // process legacy data, creating active file at starting time, then
+ // using end time to possibly trigger rotation.
+ mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
+ mRotator.maybeRotate(endMillis);
+ }
+ }
+
+ public void importLegacyUidLocked(File file) throws IOException {
+ // legacy file still exists; start empty to avoid double importing
+ mRotator.deleteAll();
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
+ collection.readLegacyUid(file, mOnlyTags);
+
+ final long startMillis = collection.getStartMillis();
+ final long endMillis = collection.getEndMillis();
+
+ if (!collection.isEmpty()) {
+ // process legacy data, creating active file at starting time, then
+ // using end time to possibly trigger rotation.
+ mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
+ mRotator.maybeRotate(endMillis);
+ }
+ }
+
+ public void dumpLocked(IndentingPrintWriter pw, boolean fullHistory) {
+ pw.print("Pending bytes: "); pw.println(mPending.getTotalBytes());
+ if (fullHistory) {
+ pw.println("Complete history:");
+ getOrLoadCompleteLocked().dump(pw);
+ } else {
+ pw.println("History since boot:");
+ mSinceBoot.dump(pw);
+ }
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index f660520..c9b67fc 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -34,14 +34,19 @@ import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
-import static android.net.TrafficStats.UID_REMOVED;
-import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
-import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE;
+import static android.provider.Settings.Secure.NETSTATS_DEV_PERSIST_BYTES;
+import static android.provider.Settings.Secure.NETSTATS_DEV_ROTATE_AGE;
+import static android.provider.Settings.Secure.NETSTATS_GLOBAL_ALERT_BYTES;
import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
-import static android.provider.Settings.Secure.NETSTATS_TAG_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_SAMPLE_ENABLED;
+import static android.provider.Settings.Secure.NETSTATS_TIME_CACHE_MAX_AGE;
import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_UID_DELETE_AGE;
+import static android.provider.Settings.Secure.NETSTATS_UID_PERSIST_BYTES;
+import static android.provider.Settings.Secure.NETSTATS_UID_ROTATE_AGE;
import static android.telephony.PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
import static android.telephony.PhoneStateListener.LISTEN_NONE;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -61,19 +66,18 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
+import android.net.LinkProperties;
import android.net.NetworkIdentity;
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkStats;
-import android.net.NetworkStats.NonMonotonicException;
+import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.TrafficStats;
import android.os.Binder;
import android.os.DropBoxManager;
import android.os.Environment;
@@ -94,32 +98,18 @@ import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TrustedTime;
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.Objects;
+import com.android.internal.util.FileRotator;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.connectivity.Tethering;
-import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.net.ProtocolException;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Random;
-
-import libcore.io.IoUtils;
/**
* Collect and persist detailed network statistics, and provide this data to
@@ -127,19 +117,12 @@ import libcore.io.IoUtils;
*/
public class NetworkStatsService extends INetworkStatsService.Stub {
private static final String TAG = "NetworkStats";
- private static final boolean LOGD = false;
- private static final boolean LOGV = false;
-
- /** File header magic number: "ANET" */
- private static final int FILE_MAGIC = 0x414E4554;
- private static final int VERSION_NETWORK_INIT = 1;
- private static final int VERSION_UID_INIT = 1;
- private static final int VERSION_UID_WITH_IDENT = 2;
- private static final int VERSION_UID_WITH_TAG = 3;
- private static final int VERSION_UID_WITH_SET = 4;
+ private static final boolean LOGD = true;
+ private static final boolean LOGV = true;
private static final int MSG_PERFORM_POLL = 1;
private static final int MSG_UPDATE_IFACES = 2;
+ private static final int MSG_REGISTER_GLOBAL_ALERT = 3;
/** Flags to control detail level of poll event. */
private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -147,9 +130,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
private static final int FLAG_PERSIST_FORCE = 0x100;
- /** 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;
@@ -159,10 +139,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private final TelephonyManager mTeleManager;
private final NetworkStatsSettings mSettings;
+ private final File mSystemDir;
+ private final File mBaseDir;
+
private final PowerManager.WakeLock mWakeLock;
private IConnectivityManager mConnManager;
- private DropBoxManager mDropBox;
// @VisibleForTesting
public static final String ACTION_NETWORK_STATS_POLL =
@@ -172,71 +154,72 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private PendingIntent mPollIntent;
- // TODO: trim empty history objects entirely
-
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
- private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
+ private static final String PREFIX_DEV = "dev";
+ private static final String PREFIX_UID = "uid";
+ private static final String PREFIX_UID_TAG = "uid_tag";
/**
* Settings that can be changed externally.
*/
public interface NetworkStatsSettings {
public long getPollInterval();
- public long getPersistThreshold();
- public long getNetworkBucketDuration();
- public long getNetworkMaxHistory();
- public long getUidBucketDuration();
- public long getUidMaxHistory();
- public long getTagMaxHistory();
public long getTimeCacheMaxAge();
+ public long getGlobalAlertBytes();
+ public boolean getSampleEnabled();
+
+ public static class Config {
+ public final long bucketDuration;
+ public final long persistBytes;
+ public final long rotateAgeMillis;
+ public final long deleteAgeMillis;
+
+ public Config(long bucketDuration, long persistBytes, long rotateAgeMillis,
+ long deleteAgeMillis) {
+ this.bucketDuration = bucketDuration;
+ this.persistBytes = persistBytes;
+ this.rotateAgeMillis = rotateAgeMillis;
+ this.deleteAgeMillis = deleteAgeMillis;
+ }
+ }
+
+ public Config getDevConfig();
+ public Config getUidConfig();
+ public Config getUidTagConfig();
}
private final Object mStatsLock = new Object();
/** Set of currently active ifaces. */
private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
- /** Set of historical {@code dev} stats for known networks. */
- private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkDevStats = Maps.newHashMap();
- /** Set of historical {@code xtables} stats for known networks. */
- private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkXtStats = Maps.newHashMap();
- /** 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;
-
- private NetworkStats mLastPollNetworkDevSnapshot;
- private NetworkStats mLastPollNetworkXtSnapshot;
- private NetworkStats mLastPollUidSnapshot;
- private NetworkStats mLastPollOperationsSnapshot;
-
- private NetworkStats mLastPersistNetworkDevSnapshot;
- private NetworkStats mLastPersistNetworkXtSnapshot;
- private NetworkStats mLastPersistUidSnapshot;
+ /** Current default active iface. */
+ private String mActiveIface;
+
+ private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
+ new DropBoxNonMonotonicObserver();
+
+ private NetworkStatsRecorder mDevRecorder;
+ private NetworkStatsRecorder mUidRecorder;
+ private NetworkStatsRecorder mUidTagRecorder;
+
+ /** Cached {@link #mDevRecorder} stats. */
+ private NetworkStatsCollection mDevStatsCached;
/** Current counter sets for each UID. */
private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
/** Data layer operation counters for splicing into other structures. */
- private NetworkStats mOperations = new NetworkStats(0L, 10);
+ private NetworkStats mUidOperations = new NetworkStats(0L, 10);
private final HandlerThread mHandlerThread;
private final Handler mHandler;
- private final AtomicFile mNetworkDevFile;
- private final AtomicFile mNetworkXtFile;
- private final AtomicFile mUidFile;
-
public NetworkStatsService(
Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
this(context, networkManager, alarmManager, NtpTrustedTime.getInstance(context),
- getSystemDir(), new DefaultNetworkStatsSettings(context));
+ getDefaultSystemDir(), new DefaultNetworkStatsSettings(context));
}
- private static File getSystemDir() {
+ private static File getDefaultSystemDir() {
return new File(Environment.getDataDirectory(), "system");
}
@@ -258,9 +241,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
- mNetworkDevFile = new AtomicFile(new File(systemDir, "netstats.bin"));
- mNetworkXtFile = new AtomicFile(new File(systemDir, "netstats_xt.bin"));
- mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin"));
+ mSystemDir = checkNotNull(systemDir);
+ mBaseDir = new File(systemDir, "netstats");
+ mBaseDir.mkdirs();
}
public void bindConnectivityManager(IConnectivityManager connManager) {
@@ -268,17 +251,27 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
public void systemReady() {
+ if (!isBandwidthControlEnabled()) {
+ Slog.w(TAG, "bandwidth controls disabled, unable to track stats");
+ return;
+ }
+
+ // create data recorders along with historical rotators
+ mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false);
+ mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false);
+ mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true);
+
synchronized (mStatsLock) {
+ // upgrade any legacy stats, migrating them to rotated files
+ maybeUpgradeLegacyStatsLocked();
+
// read historical network stats from disk, since policy service
- // might need them right away. we delay loading detailed UID stats
- // until actually needed.
- readNetworkDevStatsLocked();
- readNetworkXtStatsLocked();
- mNetworkStatsLoaded = true;
- }
+ // might need them right away.
+ mDevStatsCached = mDevRecorder.getOrLoadCompleteLocked();
- // bootstrap initial stats to prevent double-counting later
- bootstrapStats();
+ // bootstrap initial stats to prevent double-counting later
+ bootstrapStatsLocked();
+ }
// watch for network interfaces to be claimed
final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE);
@@ -312,8 +305,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
registerPollAlarmLocked();
registerGlobalAlert();
+ }
- mDropBox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
+ private NetworkStatsRecorder buildRecorder(
+ String prefix, NetworkStatsSettings.Config config, boolean includeTags) {
+ return new NetworkStatsRecorder(
+ new FileRotator(mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
+ mNonMonotonicObserver, prefix, config.bucketDuration, config.persistBytes,
+ includeTags);
}
private void shutdownLocked() {
@@ -325,18 +324,44 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mTeleManager.listen(mPhoneListener, LISTEN_NONE);
- if (mNetworkStatsLoaded) {
- writeNetworkDevStatsLocked();
- writeNetworkXtStatsLocked();
- }
- if (mUidStatsLoaded) {
- writeUidStatsLocked();
+ final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
+ : System.currentTimeMillis();
+
+ // persist any pending stats
+ mDevRecorder.forcePersistLocked(currentTime);
+ mUidRecorder.forcePersistLocked(currentTime);
+ mUidTagRecorder.forcePersistLocked(currentTime);
+
+ mDevRecorder = null;
+ mUidRecorder = null;
+ mUidTagRecorder = null;
+
+ mDevStatsCached = null;
+ }
+
+ private void maybeUpgradeLegacyStatsLocked() {
+ File file;
+ try {
+ file = new File(mSystemDir, "netstats.bin");
+ if (file.exists()) {
+ mDevRecorder.importLegacyNetworkLocked(file);
+ file.delete();
+ }
+
+ file = new File(mSystemDir, "netstats_xt.bin");
+ if (file.exists()) {
+ file.delete();
+ }
+
+ file = new File(mSystemDir, "netstats_uid.bin");
+ if (file.exists()) {
+ mUidRecorder.importLegacyUidLocked(file);
+ mUidTagRecorder.importLegacyUidLocked(file);
+ file.delete();
+ }
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem during legacy upgrade", e);
}
- mNetworkDevStats.clear();
- mNetworkXtStats.clear();
- mUidStats.clear();
- mNetworkStatsLoaded = false;
- mUidStatsLoaded = false;
}
/**
@@ -367,7 +392,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
*/
private void registerGlobalAlert() {
try {
- final long alertBytes = mSettings.getPersistThreshold();
+ final long alertBytes = mSettings.getGlobalAlertBytes();
mNetworkManager.setGlobalAlert(alertBytes);
} catch (IllegalStateException e) {
Slog.w(TAG, "problem registering for global alert: " + e);
@@ -378,161 +403,39 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- return getHistoryForNetworkDev(template, fields);
+ return mDevStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields);
}
- private NetworkStatsHistory getHistoryForNetworkDev(NetworkTemplate template, int fields) {
- return getHistoryForNetwork(template, fields, mNetworkDevStats);
- }
-
- private NetworkStatsHistory getHistoryForNetworkXt(NetworkTemplate template, int fields) {
- return getHistoryForNetwork(template, fields, mNetworkXtStats);
- }
-
- private NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields,
- HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
- synchronized (mStatsLock) {
- // combine all interfaces that match template
- final NetworkStatsHistory combined = new NetworkStatsHistory(
- mSettings.getNetworkBucketDuration(), estimateNetworkBuckets(), fields);
- for (NetworkIdentitySet ident : source.keySet()) {
- if (templateMatches(template, ident)) {
- final NetworkStatsHistory history = source.get(ident);
- if (history != null) {
- combined.recordEntireHistory(history);
- }
- }
- }
- return combined;
- }
+ @Override
+ public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) {
+ return mDevStatsCached.getSummary(template, start, end);
}
@Override
public NetworkStatsHistory getHistoryForUid(
NetworkTemplate template, int uid, int set, int tag, int fields) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
-
- synchronized (mStatsLock) {
- ensureUidStatsLoadedLocked();
-
- // combine all interfaces that match template
- final NetworkStatsHistory combined = new NetworkStatsHistory(
- mSettings.getUidBucketDuration(), estimateUidBuckets(), fields);
- for (UidStatsKey key : mUidStats.keySet()) {
- final boolean setMatches = set == SET_ALL || key.set == set;
- if (templateMatches(template, key.ident) && key.uid == uid && setMatches
- && key.tag == tag) {
- final NetworkStatsHistory history = mUidStats.get(key);
- combined.recordEntireHistory(history);
- }
- }
-
- return combined;
- }
- }
-
- @Override
- public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- return getSummaryForNetworkDev(template, start, end);
- }
-
- private NetworkStats getSummaryForNetworkDev(NetworkTemplate template, long start, long end) {
- return getSummaryForNetwork(template, start, end, mNetworkDevStats);
- }
-
- private NetworkStats getSummaryForNetworkXt(NetworkTemplate template, long start, long end) {
- return getSummaryForNetwork(template, start, end, mNetworkXtStats);
- }
-
- private NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end,
- HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
- synchronized (mStatsLock) {
- // use system clock to be externally consistent
- final long now = System.currentTimeMillis();
-
- final NetworkStats stats = new NetworkStats(end - start, 1);
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- NetworkStatsHistory.Entry historyEntry = null;
-
- // combine total from all interfaces that match template
- for (NetworkIdentitySet ident : source.keySet()) {
- if (templateMatches(template, ident)) {
- final NetworkStatsHistory history = source.get(ident);
- historyEntry = history.getValues(start, end, now, historyEntry);
-
- entry.iface = IFACE_ALL;
- entry.uid = UID_ALL;
- entry.tag = TAG_NONE;
- entry.rxBytes = historyEntry.rxBytes;
- entry.rxPackets = historyEntry.rxPackets;
- entry.txBytes = historyEntry.txBytes;
- entry.txPackets = historyEntry.txPackets;
-
- stats.combineValues(entry);
- }
- }
-
- return stats;
- }
- }
-
- 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());
- }
+ // TODO: transition to stats sessions to avoid WeakReferences
+ if (tag == TAG_NONE) {
+ return mUidRecorder.getOrLoadCompleteLocked().getHistory(
+ template, uid, set, tag, fields);
+ } else {
+ return mUidTagRecorder.getOrLoadCompleteLocked().getHistory(
+ template, uid, set, tag, fields);
}
- return start;
}
@Override
public NetworkStats getSummaryForAllUid(
NetworkTemplate template, long start, long end, boolean includeTags) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
-
- synchronized (mStatsLock) {
- ensureUidStatsLoadedLocked();
-
- // use system clock to be externally consistent
- final long now = System.currentTimeMillis();
-
- final NetworkStats stats = new NetworkStats(end - start, 24);
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- NetworkStatsHistory.Entry historyEntry = null;
-
- for (UidStatsKey key : mUidStats.keySet()) {
- if (templateMatches(template, key.ident)) {
- // always include summary under TAG_NONE, and include
- // other tags when requested.
- if (key.tag == TAG_NONE || includeTags) {
- final NetworkStatsHistory history = mUidStats.get(key);
- historyEntry = history.getValues(start, end, now, historyEntry);
-
- entry.iface = IFACE_ALL;
- entry.uid = key.uid;
- entry.set = key.set;
- entry.tag = key.tag;
- entry.rxBytes = historyEntry.rxBytes;
- entry.rxPackets = historyEntry.rxPackets;
- entry.txBytes = historyEntry.txBytes;
- entry.txPackets = historyEntry.txPackets;
- entry.operations = historyEntry.operations;
-
- if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
- || entry.txPackets > 0 || entry.operations > 0) {
- stats.combineValues(entry);
- }
- }
- }
- }
-
- return stats;
- }
+ // TODO: transition to stats sessions to avoid WeakReferences
+ final NetworkStats stats = mUidRecorder.getOrLoadCompleteLocked().getSummary(
+ template, start, end);
+ if (includeTags) {
+ final NetworkStats tagStats = mUidTagRecorder.getOrLoadCompleteLocked().getSummary(
+ template, start, end);
+ stats.combineAllValues(tagStats);
+ }
+ return stats;
}
@Override
@@ -543,7 +446,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO: switch to data layer stats once kernel exports
// for now, read network layer stats and flatten across all ifaces
- final NetworkStats networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid);
+ final long token = Binder.clearCallingIdentity();
+ final NetworkStats networkLayer;
+ try {
+ networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
final NetworkStats dataLayer = new NetworkStats(
networkLayer.getElapsedRealtime(), networkLayer.size());
@@ -555,7 +465,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
// splice in operation counts
- dataLayer.spliceOperationsFrom(mOperations);
+ dataLayer.spliceOperationsFrom(mUidOperations);
return dataLayer;
}
@@ -574,8 +484,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
synchronized (mStatsLock) {
final int set = mActiveUidCounterSet.get(uid, SET_DEFAULT);
- mOperations.combineValues(IFACE_ALL, uid, set, tag, 0L, 0L, 0L, 0L, operationCount);
- mOperations.combineValues(IFACE_ALL, uid, set, TAG_NONE, 0L, 0L, 0L, 0L, operationCount);
+ mUidOperations.combineValues(
+ mActiveIface, uid, set, tag, 0L, 0L, 0L, 0L, operationCount);
+ mUidOperations.combineValues(
+ mActiveIface, uid, set, TAG_NONE, 0L, 0L, 0L, 0L, operationCount);
}
}
@@ -596,7 +508,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public void forceUpdate() {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- performPoll(FLAG_PERSIST_ALL);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ performPoll(FLAG_PERSIST_ALL);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -680,7 +598,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
// re-arm global alert for next update
- registerGlobalAlert();
+ mHandler.obtainMessage(MSG_REGISTER_GLOBAL_ALERT).sendToTarget();
}
}
};
@@ -743,13 +661,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
performPollLocked(FLAG_PERSIST_NETWORK);
final NetworkState[] states;
+ final LinkProperties activeLink;
try {
states = mConnManager.getAllNetworkState();
+ activeLink = mConnManager.getActiveLinkProperties();
} catch (RemoteException e) {
// ignored; service lives in system_server
return;
}
+ mActiveIface = activeLink != null ? activeLink.getInterfaceName() : null;
+
// rebuild active interfaces based on connected networks
mActiveIfaces.clear();
@@ -773,12 +695,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* Bootstrap initial stats snapshot, usually during {@link #systemReady()}
* so we have baseline values without double-counting.
*/
- private void bootstrapStats() {
+ private void bootstrapStatsLocked() {
+ final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
+ : System.currentTimeMillis();
+
try {
- mLastPollUidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
- mLastPollNetworkDevSnapshot = mNetworkManager.getNetworkStatsSummary();
- mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
- mLastPollOperationsSnapshot = new NetworkStats(0L, 0);
+ // snapshot and record current counters; read UID stats first to
+ // avoid overcounting dev stats.
+ final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
+ final NetworkStats devSnapshot = getNetworkStatsSummary();
+
+ mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
+ mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
+ mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
+
} catch (IllegalStateException e) {
Slog.w(TAG, "problem reading network stats: " + e);
} catch (RemoteException e) {
@@ -818,27 +748,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO: consider marking "untrusted" times in historical stats
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
- final long threshold = mSettings.getPersistThreshold();
- final NetworkStats uidSnapshot;
- final NetworkStats networkXtSnapshot;
- final NetworkStats networkDevSnapshot;
try {
- // collect any tethering stats
- final NetworkStats tetherSnapshot = getNetworkStatsTethering();
-
- // record uid stats, folding in tethering stats
- uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
- uidSnapshot.combineAllValues(tetherSnapshot);
- performUidPollLocked(uidSnapshot, currentTime);
-
- // record dev network stats
- networkDevSnapshot = mNetworkManager.getNetworkStatsSummary();
- performNetworkDevPollLocked(networkDevSnapshot, currentTime);
+ // snapshot and record current counters; read UID stats first to
+ // avoid overcounting dev stats.
+ final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
+ final NetworkStats devSnapshot = getNetworkStatsSummary();
- // record xt network stats
- networkXtSnapshot = computeNetworkXtSnapshotFromUid(uidSnapshot);
- performNetworkXtPollLocked(networkXtSnapshot, currentTime);
+ mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
+ mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
+ mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e);
@@ -848,26 +767,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return;
}
- // persist when enough network data has occurred
- final long persistNetworkDevDelta = computeStatsDelta(
- mLastPersistNetworkDevSnapshot, networkDevSnapshot, true, "devp").getTotalBytes();
- final long persistNetworkXtDelta = computeStatsDelta(
- mLastPersistNetworkXtSnapshot, networkXtSnapshot, true, "xtp").getTotalBytes();
- final boolean networkOverThreshold = persistNetworkDevDelta > threshold
- || persistNetworkXtDelta > threshold;
- if (persistForce || (persistNetwork && networkOverThreshold)) {
- writeNetworkDevStatsLocked();
- writeNetworkXtStatsLocked();
- mLastPersistNetworkDevSnapshot = networkDevSnapshot;
- mLastPersistNetworkXtSnapshot = networkXtSnapshot;
- }
-
- // persist when enough uid data has occurred
- final long persistUidDelta = computeStatsDelta(
- mLastPersistUidSnapshot, uidSnapshot, true, "uidp").getTotalBytes();
- if (persistForce || (persistUid && persistUidDelta > threshold)) {
- writeUidStatsLocked();
- mLastPersistUidSnapshot = uidSnapshot;
+ // persist any pending data depending on requested flags
+ if (persistForce) {
+ mDevRecorder.forcePersistLocked(currentTime);
+ mUidRecorder.forcePersistLocked(currentTime);
+ mUidTagRecorder.forcePersistLocked(currentTime);
+ } else {
+ if (persistNetwork) {
+ mDevRecorder.maybePersistLocked(currentTime);
+ }
+ if (persistUid) {
+ mUidRecorder.maybePersistLocked(currentTime);
+ mUidTagRecorder.maybePersistLocked(currentTime);
+ }
}
if (LOGV) {
@@ -875,9 +787,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
Slog.v(TAG, "performPollLocked() took " + duration + "ms");
}
- if (ENABLE_SAMPLE_AFTER_POLL) {
+ if (mSettings.getSampleEnabled()) {
// sample stats after each full poll
- performSample();
+ performSampleLocked();
}
// finally, dispatch updated event to any listeners
@@ -887,511 +799,58 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
/**
- * Update {@link #mNetworkDevStats} historical usage.
- */
- private void performNetworkDevPollLocked(NetworkStats networkDevSnapshot, long currentTime) {
- final HashSet<String> unknownIface = Sets.newHashSet();
-
- final NetworkStats delta = computeStatsDelta(
- mLastPollNetworkDevSnapshot, networkDevSnapshot, false, "dev");
- final long timeStart = currentTime - delta.getElapsedRealtime();
-
- NetworkStats.Entry entry = null;
- for (int i = 0; i < delta.size(); i++) {
- entry = delta.getValues(i, entry);
- final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
- if (ident == null) {
- unknownIface.add(entry.iface);
- continue;
- }
-
- final NetworkStatsHistory history = findOrCreateNetworkDevStatsLocked(ident);
- history.recordData(timeStart, currentTime, entry);
- }
-
- mLastPollNetworkDevSnapshot = networkDevSnapshot;
-
- if (LOGD && unknownIface.size() > 0) {
- Slog.w(TAG, "unknown dev interfaces " + unknownIface + ", ignoring those stats");
- }
- }
-
- /**
- * Update {@link #mNetworkXtStats} historical usage.
- */
- private void performNetworkXtPollLocked(NetworkStats networkXtSnapshot, long currentTime) {
- final HashSet<String> unknownIface = Sets.newHashSet();
-
- final NetworkStats delta = computeStatsDelta(
- mLastPollNetworkXtSnapshot, networkXtSnapshot, false, "xt");
- final long timeStart = currentTime - delta.getElapsedRealtime();
-
- NetworkStats.Entry entry = null;
- for (int i = 0; i < delta.size(); i++) {
- entry = delta.getValues(i, entry);
- final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
- if (ident == null) {
- unknownIface.add(entry.iface);
- continue;
- }
-
- final NetworkStatsHistory history = findOrCreateNetworkXtStatsLocked(ident);
- history.recordData(timeStart, currentTime, entry);
- }
-
- mLastPollNetworkXtSnapshot = networkXtSnapshot;
-
- if (LOGD && unknownIface.size() > 0) {
- Slog.w(TAG, "unknown xt interfaces " + unknownIface + ", ignoring those stats");
- }
- }
-
- /**
- * Update {@link #mUidStats} historical usage.
- */
- private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
- ensureUidStatsLoadedLocked();
-
- final NetworkStats delta = computeStatsDelta(
- mLastPollUidSnapshot, uidSnapshot, false, "uid");
- final NetworkStats operationsDelta = computeStatsDelta(
- mLastPollOperationsSnapshot, mOperations, false, "uidop");
- final long timeStart = currentTime - delta.getElapsedRealtime();
-
- NetworkStats.Entry entry = null;
- NetworkStats.Entry operationsEntry = null;
- for (int i = 0; i < delta.size(); i++) {
- entry = delta.getValues(i, entry);
- final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
- if (ident == null) {
- if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
- || entry.txPackets > 0) {
- Log.w(TAG, "dropping UID delta from unknown iface: " + entry);
- }
- continue;
- }
-
- // splice in operation counts since last poll
- final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.set, entry.tag);
- if (j != -1) {
- operationsEntry = operationsDelta.getValues(j, operationsEntry);
- entry.operations = operationsEntry.operations;
- }
-
- final NetworkStatsHistory history = findOrCreateUidStatsLocked(
- ident, entry.uid, entry.set, entry.tag);
- history.recordData(timeStart, currentTime, entry);
- }
-
- mLastPollUidSnapshot = uidSnapshot;
- mLastPollOperationsSnapshot = mOperations.clone();
- }
-
- /**
* Sample recent statistics summary into {@link EventLog}.
*/
- private void performSample() {
- final long largestBucketSize = Math.max(
- mSettings.getNetworkBucketDuration(), mSettings.getUidBucketDuration());
-
- // take sample as atomic buckets
- final long now = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
- final long end = now - (now % largestBucketSize) + largestBucketSize;
- final long start = end - largestBucketSize;
-
+ private void performSampleLocked() {
+ // TODO: migrate trustedtime fixes to separate binary log events
final long trustedTime = mTime.hasCache() ? mTime.currentTimeMillis() : -1;
- long devHistoryStart = Long.MAX_VALUE;
- NetworkTemplate template = null;
- NetworkStats.Entry devTotal = null;
- NetworkStats.Entry xtTotal = null;
- NetworkStats.Entry uidTotal = null;
+ NetworkTemplate template;
+ NetworkStats.Entry devTotal;
+ NetworkStats.Entry xtTotal;
+ NetworkStats.Entry uidTotal;
// 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);
+ devTotal = mDevRecorder.getTotalSinceBootLocked(template);
+ xtTotal = new NetworkStats.Entry();
+ uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
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, devHistoryStart);
+ trustedTime);
// 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);
+ devTotal = mDevRecorder.getTotalSinceBootLocked(template);
+ xtTotal = new NetworkStats.Entry();
+ uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
+
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, devHistoryStart);
+ trustedTime);
}
/**
- * Clean up {@link #mUidStats} after UID is removed.
+ * Clean up {@link #mUidRecorder} after UID is removed.
*/
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());
-
- // migrate all UID stats into special "removed" bucket
- for (UidStatsKey key : knownKeys) {
- if (key.uid == uid) {
- // only migrate combined TAG_NONE history
- if (key.tag == TAG_NONE) {
- final NetworkStatsHistory uidHistory = mUidStats.get(key);
- final NetworkStatsHistory removedHistory = findOrCreateUidStatsLocked(
- key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
- removedHistory.recordEntireHistory(uidHistory);
- }
- mUidStats.remove(key);
- }
- }
-
- // clear UID from current stats snapshot
- if (mLastPollUidSnapshot != null) {
- mLastPollUidSnapshot = mLastPollUidSnapshot.withoutUid(uid);
- mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
- }
+ mUidRecorder.removeUidLocked(uid);
+ mUidTagRecorder.removeUidLocked(uid);
// clear kernel stats associated with UID
resetKernelUidStats(uid);
-
- // since this was radical rewrite, push to disk
- writeUidStatsLocked();
- }
-
- private NetworkStatsHistory findOrCreateNetworkXtStatsLocked(NetworkIdentitySet ident) {
- return findOrCreateNetworkStatsLocked(ident, mNetworkXtStats);
- }
-
- private NetworkStatsHistory findOrCreateNetworkDevStatsLocked(NetworkIdentitySet ident) {
- return findOrCreateNetworkStatsLocked(ident, mNetworkDevStats);
- }
-
- private NetworkStatsHistory findOrCreateNetworkStatsLocked(
- NetworkIdentitySet ident, HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
- final NetworkStatsHistory existing = source.get(ident);
-
- // update when no existing, or when bucket duration changed
- final long bucketDuration = mSettings.getNetworkBucketDuration();
- NetworkStatsHistory updated = null;
- if (existing == null) {
- updated = new NetworkStatsHistory(bucketDuration, 10);
- } else if (existing.getBucketDuration() != bucketDuration) {
- updated = new NetworkStatsHistory(
- bucketDuration, estimateResizeBuckets(existing, bucketDuration));
- updated.recordEntireHistory(existing);
- }
-
- if (updated != null) {
- source.put(ident, updated);
- return updated;
- } else {
- return existing;
- }
- }
-
- private NetworkStatsHistory findOrCreateUidStatsLocked(
- NetworkIdentitySet ident, int uid, int set, int tag) {
- ensureUidStatsLoadedLocked();
-
- final UidStatsKey key = new UidStatsKey(ident, uid, set, tag);
- final NetworkStatsHistory existing = mUidStats.get(key);
-
- // update when no existing, or when bucket duration changed
- final long bucketDuration = mSettings.getUidBucketDuration();
- NetworkStatsHistory updated = null;
- if (existing == null) {
- updated = new NetworkStatsHistory(bucketDuration, 10);
- } else if (existing.getBucketDuration() != bucketDuration) {
- updated = new NetworkStatsHistory(
- bucketDuration, estimateResizeBuckets(existing, bucketDuration));
- updated.recordEntireHistory(existing);
- }
-
- if (updated != null) {
- mUidStats.put(key, updated);
- return updated;
- } else {
- return existing;
- }
- }
-
- private void readNetworkDevStatsLocked() {
- if (LOGV) Slog.v(TAG, "readNetworkDevStatsLocked()");
- readNetworkStats(mNetworkDevFile, mNetworkDevStats);
- }
-
- private void readNetworkXtStatsLocked() {
- if (LOGV) Slog.v(TAG, "readNetworkXtStatsLocked()");
- readNetworkStats(mNetworkXtFile, mNetworkXtStats);
- }
-
- private static void readNetworkStats(
- AtomicFile inputFile, HashMap<NetworkIdentitySet, NetworkStatsHistory> output) {
- // clear any existing stats and read from disk
- output.clear();
-
- DataInputStream in = null;
- try {
- in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
-
- // verify file magic header intact
- final int magic = in.readInt();
- if (magic != FILE_MAGIC) {
- throw new ProtocolException("unexpected magic: " + magic);
- }
-
- final int version = in.readInt();
- switch (version) {
- case VERSION_NETWORK_INIT: {
- // network := size *(NetworkIdentitySet NetworkStatsHistory)
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- final NetworkIdentitySet ident = new NetworkIdentitySet(in);
- final NetworkStatsHistory history = new NetworkStatsHistory(in);
- output.put(ident, history);
- }
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- } catch (FileNotFoundException e) {
- // missing stats is okay, probably first boot
- } catch (IOException e) {
- Log.wtf(TAG, "problem reading network stats", e);
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
-
- private void ensureUidStatsLoadedLocked() {
- if (!mUidStatsLoaded) {
- readUidStatsLocked();
- mUidStatsLoaded = true;
- }
- }
-
- private void readUidStatsLocked() {
- if (LOGV) Slog.v(TAG, "readUidStatsLocked()");
-
- // clear any existing stats and read from disk
- mUidStats.clear();
-
- DataInputStream in = null;
- try {
- in = new DataInputStream(new BufferedInputStream(mUidFile.openRead()));
-
- // verify file magic header intact
- final int magic = in.readInt();
- if (magic != FILE_MAGIC) {
- throw new ProtocolException("unexpected magic: " + magic);
- }
-
- final int version = in.readInt();
- switch (version) {
- case VERSION_UID_INIT: {
- // uid := size *(UID NetworkStatsHistory)
-
- // drop this data version, since we don't have a good
- // mapping into NetworkIdentitySet.
- break;
- }
- case VERSION_UID_WITH_IDENT: {
- // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
-
- // drop this data version, since this version only existed
- // for a short time.
- break;
- }
- case VERSION_UID_WITH_TAG:
- case VERSION_UID_WITH_SET: {
- // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
- final int identSize = in.readInt();
- for (int i = 0; i < identSize; i++) {
- final NetworkIdentitySet ident = new NetworkIdentitySet(in);
-
- final int size = in.readInt();
- for (int j = 0; j < size; j++) {
- final int uid = in.readInt();
- final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
- : SET_DEFAULT;
- final int tag = in.readInt();
-
- final UidStatsKey key = new UidStatsKey(ident, uid, set, tag);
- final NetworkStatsHistory history = new NetworkStatsHistory(in);
- mUidStats.put(key, history);
- }
- }
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- } catch (FileNotFoundException e) {
- // missing stats is okay, probably first boot
- } catch (IOException e) {
- Log.wtf(TAG, "problem reading uid stats", e);
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
-
- private void writeNetworkDevStatsLocked() {
- if (LOGV) Slog.v(TAG, "writeNetworkDevStatsLocked()");
- writeNetworkStats(mNetworkDevStats, mNetworkDevFile);
- }
-
- private void writeNetworkXtStatsLocked() {
- if (LOGV) Slog.v(TAG, "writeNetworkXtStatsLocked()");
- writeNetworkStats(mNetworkXtStats, mNetworkXtFile);
- }
-
- private void writeNetworkStats(
- HashMap<NetworkIdentitySet, NetworkStatsHistory> input, AtomicFile outputFile) {
- // TODO: consider duplicating stats and releasing lock while writing
-
- // trim any history beyond max
- if (mTime.hasCache()) {
- 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());
- }
- }
- }
-
- FileOutputStream fos = null;
- try {
- fos = outputFile.startWrite();
- final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
-
- out.writeInt(FILE_MAGIC);
- out.writeInt(VERSION_NETWORK_INIT);
-
- out.writeInt(input.size());
- for (NetworkIdentitySet ident : input.keySet()) {
- final NetworkStatsHistory history = input.get(ident);
- ident.writeToStream(out);
- history.writeToStream(out);
- }
-
- out.flush();
- outputFile.finishWrite(fos);
- } catch (IOException e) {
- Log.wtf(TAG, "problem writing stats", e);
- if (fos != null) {
- outputFile.failWrite(fos);
- }
- }
- }
-
- private void writeUidStatsLocked() {
- if (LOGV) Slog.v(TAG, "writeUidStatsLocked()");
-
- if (!mUidStatsLoaded) {
- Slog.w(TAG, "asked to write UID stats when not loaded; skipping");
- return;
- }
-
- // TODO: consider duplicating stats and releasing lock while writing
-
- // trim any history beyond max
- if (mTime.hasCache()) {
- final long currentTime = Math.min(
- System.currentTimeMillis(), mTime.currentTimeMillis());
- final long maxUidHistory = mSettings.getUidMaxHistory();
- final long maxTagHistory = mSettings.getTagMaxHistory();
- for (UidStatsKey key : mUidStats.keySet()) {
- final NetworkStatsHistory history = mUidStats.get(key);
-
- // detailed tags are trimmed sooner than summary in TAG_NONE
- if (key.tag == TAG_NONE) {
- history.removeBucketsBefore(currentTime - maxUidHistory);
- } else {
- history.removeBucketsBefore(currentTime - maxTagHistory);
- }
- }
- }
-
- // build UidStatsKey lists grouped by ident
- final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap();
- for (UidStatsKey key : mUidStats.keySet()) {
- ArrayList<UidStatsKey> keys = keysByIdent.get(key.ident);
- if (keys == null) {
- keys = Lists.newArrayList();
- keysByIdent.put(key.ident, keys);
- }
- keys.add(key);
- }
-
- FileOutputStream fos = null;
- try {
- fos = mUidFile.startWrite();
- final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
-
- out.writeInt(FILE_MAGIC);
- out.writeInt(VERSION_UID_WITH_SET);
-
- out.writeInt(keysByIdent.size());
- for (NetworkIdentitySet ident : keysByIdent.keySet()) {
- final ArrayList<UidStatsKey> keys = keysByIdent.get(ident);
- ident.writeToStream(out);
-
- out.writeInt(keys.size());
- for (UidStatsKey key : keys) {
- final NetworkStatsHistory history = mUidStats.get(key);
- out.writeInt(key.uid);
- out.writeInt(key.set);
- out.writeInt(key.tag);
- history.writeToStream(out);
- }
- }
-
- out.flush();
- mUidFile.finishWrite(fos);
- } catch (IOException e) {
- Log.wtf(TAG, "problem writing stats", e);
- if (fos != null) {
- mUidFile.failWrite(fos);
- }
- }
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
final HashSet<String> argSet = new HashSet<String>();
@@ -1399,182 +858,68 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
argSet.add(arg);
}
- final boolean fullHistory = argSet.contains("full");
+ // usage: dumpsys netstats --full --uid --tag
+ final boolean poll = argSet.contains("--poll") || argSet.contains("poll");
+ final boolean fullHistory = argSet.contains("--full") || argSet.contains("full");
+ final boolean includeUid = argSet.contains("--uid") || argSet.contains("detail");
+ final boolean includeTag = argSet.contains("--tag") || argSet.contains("detail");
- synchronized (mStatsLock) {
- // TODO: remove this testing code, since it corrupts stats
- if (argSet.contains("generate")) {
- generateRandomLocked(args);
- pw.println("Generated stub stats");
- return;
- }
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- if (argSet.contains("poll")) {
+ synchronized (mStatsLock) {
+ if (poll) {
performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
pw.println("Forced poll");
return;
}
pw.println("Active interfaces:");
+ pw.increaseIndent();
for (String iface : mActiveIfaces.keySet()) {
final NetworkIdentitySet ident = mActiveIfaces.get(iface);
- pw.print(" iface="); pw.print(iface);
+ pw.print("iface="); pw.print(iface);
pw.print(" ident="); pw.println(ident.toString());
}
-
- pw.println("Known historical dev stats:");
- for (NetworkIdentitySet ident : mNetworkDevStats.keySet()) {
- final NetworkStatsHistory history = mNetworkDevStats.get(ident);
- pw.print(" ident="); pw.println(ident.toString());
- history.dump(" ", pw, fullHistory);
+ pw.decreaseIndent();
+
+ pw.println("Dev stats:");
+ pw.increaseIndent();
+ mDevRecorder.dumpLocked(pw, fullHistory);
+ pw.decreaseIndent();
+
+ if (includeUid) {
+ pw.println("UID stats:");
+ pw.increaseIndent();
+ mUidRecorder.dumpLocked(pw, fullHistory);
+ pw.decreaseIndent();
}
- pw.println("Known historical xt stats:");
- for (NetworkIdentitySet ident : mNetworkXtStats.keySet()) {
- final NetworkStatsHistory history = mNetworkXtStats.get(ident);
- pw.print(" ident="); pw.println(ident.toString());
- history.dump(" ", pw, fullHistory);
- }
-
- if (argSet.contains("detail")) {
- // since explicitly requested with argument, we're okay to load
- // from disk if not already in memory.
- ensureUidStatsLoadedLocked();
-
- final ArrayList<UidStatsKey> keys = Lists.newArrayList();
- keys.addAll(mUidStats.keySet());
- Collections.sort(keys);
-
- pw.println("Detailed UID stats:");
- for (UidStatsKey key : keys) {
- pw.print(" ident="); pw.print(key.ident.toString());
- pw.print(" uid="); pw.print(key.uid);
- pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
- pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
-
- final NetworkStatsHistory history = mUidStats.get(key);
- history.dump(" ", pw, fullHistory);
- }
+ if (includeTag) {
+ pw.println("UID tag stats:");
+ pw.increaseIndent();
+ mUidTagRecorder.dumpLocked(pw, fullHistory);
+ pw.decreaseIndent();
}
}
}
- /**
- * @deprecated only for temporary testing
- */
- @Deprecated
- private void generateRandomLocked(String[] args) {
- final long totalBytes = Long.parseLong(args[1]);
- final long totalTime = Long.parseLong(args[2]);
-
- final PackageManager pm = mContext.getPackageManager();
- final ArrayList<Integer> specialUidList = Lists.newArrayList();
- for (int i = 3; i < args.length; i++) {
- try {
- specialUidList.add(pm.getApplicationInfo(args[i], 0).uid);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- final HashSet<Integer> otherUidSet = Sets.newHashSet();
- for (ApplicationInfo info : pm.getInstalledApplications(0)) {
- if (pm.checkPermission(android.Manifest.permission.INTERNET, info.packageName)
- == PackageManager.PERMISSION_GRANTED && !specialUidList.contains(info.uid)) {
- otherUidSet.add(info.uid);
- }
- }
-
- final ArrayList<Integer> otherUidList = new ArrayList<Integer>(otherUidSet);
-
- final long end = System.currentTimeMillis();
- final long start = end - totalTime;
-
- mNetworkDevStats.clear();
- mNetworkXtStats.clear();
- mUidStats.clear();
-
- final Random r = new Random();
- for (NetworkIdentitySet ident : mActiveIfaces.values()) {
- final NetworkStatsHistory devHistory = findOrCreateNetworkDevStatsLocked(ident);
- final NetworkStatsHistory xtHistory = findOrCreateNetworkXtStatsLocked(ident);
-
- final ArrayList<Integer> uidList = new ArrayList<Integer>();
- uidList.addAll(specialUidList);
-
- if (uidList.size() == 0) {
- Collections.shuffle(otherUidList);
- uidList.addAll(otherUidList);
- }
-
- boolean first = true;
- long remainingBytes = totalBytes;
- for (int uid : uidList) {
- final NetworkStatsHistory defaultHistory = findOrCreateUidStatsLocked(
- ident, uid, SET_DEFAULT, TAG_NONE);
- final NetworkStatsHistory foregroundHistory = findOrCreateUidStatsLocked(
- ident, uid, SET_FOREGROUND, TAG_NONE);
-
- final long uidBytes = totalBytes / uidList.size();
-
- final float fractionDefault = r.nextFloat();
- final long defaultBytes = (long) (uidBytes * fractionDefault);
- final long foregroundBytes = (long) (uidBytes * (1 - fractionDefault));
-
- defaultHistory.generateRandom(start, end, defaultBytes);
- foregroundHistory.generateRandom(start, end, foregroundBytes);
-
- if (first) {
- final long bumpTime = (start + end) / 2;
- defaultHistory.recordData(
- bumpTime, bumpTime + DAY_IN_MILLIS, 200 * MB_IN_BYTES, 0);
- first = false;
- }
-
- devHistory.recordEntireHistory(defaultHistory);
- devHistory.recordEntireHistory(foregroundHistory);
- xtHistory.recordEntireHistory(defaultHistory);
- xtHistory.recordEntireHistory(foregroundHistory);
- }
- }
+ private NetworkStats getNetworkStatsSummary() throws RemoteException {
+ return mNetworkManager.getNetworkStatsSummary();
}
/**
- * Return the delta between two {@link NetworkStats} snapshots, where {@code
- * before} can be {@code null}.
+ * Return snapshot of current UID statistics, including any
+ * {@link TrafficStats#UID_TETHERING} and {@link #mUidOperations} values.
*/
- private NetworkStats computeStatsDelta(
- NetworkStats before, NetworkStats current, boolean collectStale, String type) {
- if (before != null) {
- 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());
+ private NetworkStats getNetworkStatsUidDetail() throws RemoteException {
+ final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
- 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;
- } else {
- // this is first snapshot; to prevent from double-counting we only
- // observe traffic occuring between known snapshots.
- return new NetworkStats(0L, 10);
- }
+ // fold tethering stats and operations into uid snapshot
+ final NetworkStats tetherSnapshot = getNetworkStatsTethering();
+ uidSnapshot.combineAllValues(tetherSnapshot);
+ uidSnapshot.combineAllValues(mUidOperations);
+
+ return uidSnapshot;
}
/**
@@ -1591,35 +936,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
- private static NetworkStats computeNetworkXtSnapshotFromUid(NetworkStats uidSnapshot) {
- return uidSnapshot.groupedByIface();
- }
-
- private int estimateNetworkBuckets() {
- return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
- }
-
- private int estimateUidBuckets() {
- return (int) (mSettings.getUidMaxHistory() / mSettings.getUidBucketDuration());
- }
-
- private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) {
- return (int) (existing.size() * existing.getBucketDuration() / newBucketDuration);
- }
-
- /**
- * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
- * in the given {@link NetworkIdentitySet}.
- */
- private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
- for (NetworkIdentity ident : identSet) {
- if (template.matches(ident)) {
- return true;
- }
- }
- return false;
- }
-
private Handler.Callback mHandlerCallback = new Handler.Callback() {
/** {@inheritDoc} */
public boolean handleMessage(Message msg) {
@@ -1633,6 +949,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
updateIfaces();
return true;
}
+ case MSG_REGISTER_GLOBAL_ALERT: {
+ registerGlobalAlert();
+ return true;
+ }
default: {
return false;
}
@@ -1646,40 +966,31 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return telephony.getSubscriberId();
}
- /**
- * Key uniquely identifying a {@link NetworkStatsHistory} for a UID.
- */
- private static class UidStatsKey implements Comparable<UidStatsKey> {
- public final NetworkIdentitySet ident;
- public final int uid;
- public final int set;
- public final int tag;
-
- public UidStatsKey(NetworkIdentitySet ident, int uid, int set, int tag) {
- this.ident = ident;
- this.uid = uid;
- this.set = set;
- this.tag = tag;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(ident, uid, set, tag);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof UidStatsKey) {
- final UidStatsKey key = (UidStatsKey) obj;
- return Objects.equal(ident, key.ident) && uid == key.uid && set == key.set
- && tag == key.tag;
- }
+ private boolean isBandwidthControlEnabled() {
+ try {
+ return mNetworkManager.isBandwidthControlEnabled();
+ } catch (RemoteException e) {
+ // ignored; service lives in system_server
return false;
}
+ }
+ private class DropBoxNonMonotonicObserver implements NonMonotonicObserver<String> {
/** {@inheritDoc} */
- public int compareTo(UidStatsKey another) {
- return Integer.compare(uid, another.uid);
+ public void foundNonMonotonic(NetworkStats left, int leftIndex, NetworkStats right,
+ int rightIndex, String cookie) {
+ Log.w(TAG, "found non-monotonic values; saving to dropbox");
+
+ // record error for debugging
+ final StringBuilder builder = new StringBuilder();
+ builder.append("found non-monotonic " + cookie + " values at left[" + leftIndex
+ + "] - right[" + rightIndex + "]\n");
+ builder.append("left=").append(left).append('\n');
+ builder.append("right=").append(right).append('\n');
+
+ final DropBoxManager dropBox = (DropBoxManager) mContext.getSystemService(
+ Context.DROPBOX_SERVICE);
+ dropBox.addText(TAG_NETSTATS_ERROR, builder.toString());
}
}
@@ -1705,26 +1016,35 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public long getPollInterval() {
return getSecureLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
}
- public long getPersistThreshold() {
- return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 2 * MB_IN_BYTES);
- }
- public long getNetworkBucketDuration() {
- return getSecureLong(NETSTATS_NETWORK_BUCKET_DURATION, HOUR_IN_MILLIS);
+ public long getTimeCacheMaxAge() {
+ return getSecureLong(NETSTATS_TIME_CACHE_MAX_AGE, DAY_IN_MILLIS);
}
- public long getNetworkMaxHistory() {
- return getSecureLong(NETSTATS_NETWORK_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+ public long getGlobalAlertBytes() {
+ return getSecureLong(NETSTATS_GLOBAL_ALERT_BYTES, 2 * MB_IN_BYTES);
}
- public long getUidBucketDuration() {
- return getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS);
+ public boolean getSampleEnabled() {
+ return getSecureBoolean(NETSTATS_SAMPLE_ENABLED, true);
}
- public long getUidMaxHistory() {
- return getSecureLong(NETSTATS_UID_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+
+ public Config getDevConfig() {
+ return new Config(getSecureLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
+ getSecureLong(NETSTATS_DEV_PERSIST_BYTES, 2 * MB_IN_BYTES),
+ getSecureLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
+ getSecureLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
}
- public long getTagMaxHistory() {
- return getSecureLong(NETSTATS_TAG_MAX_HISTORY, 30 * DAY_IN_MILLIS);
+
+ public Config getUidConfig() {
+ return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
+ getSecureLong(NETSTATS_UID_PERSIST_BYTES, 2 * MB_IN_BYTES),
+ getSecureLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
+ getSecureLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
}
- public long getTimeCacheMaxAge() {
- return DAY_IN_MILLIS;
+
+ public Config getUidTagConfig() {
+ return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
+ getSecureLong(NETSTATS_UID_PERSIST_BYTES, 2 * MB_IN_BYTES),
+ getSecureLong(NETSTATS_UID_ROTATE_AGE, 5 * DAY_IN_MILLIS),
+ getSecureLong(NETSTATS_UID_DELETE_AGE, 15 * DAY_IN_MILLIS));
}
}
}
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 11ccd60..9b1973e 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -277,6 +277,27 @@ class Installer {
return execute(builder.toString());
}
+ /**
+ * Clone all the package data directories from srcUserId to targetUserId. If copyData is true,
+ * some of the data is also copied, otherwise just empty directories are created with the
+ * correct access rights.
+ * @param srcUserId user to copy the data directories from
+ * @param targetUserId user to copy the data directories to
+ * @param copyData whether the data itself is to be copied. If false, empty directories are
+ * created.
+ * @return success/error code
+ */
+ public int cloneUserData(int srcUserId, int targetUserId, boolean copyData) {
+ StringBuilder builder = new StringBuilder("cloneuserdata");
+ builder.append(' ');
+ builder.append(srcUserId);
+ builder.append(' ');
+ builder.append(targetUserId);
+ builder.append(' ');
+ builder.append(copyData ? '1' : '0');
+ return execute(builder.toString());
+ }
+
public boolean ping() {
if (execute("ping") < 0) {
return false;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 6b61c47..38c128c 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -92,6 +92,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserId;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -620,11 +621,11 @@ public class PackageManagerService extends IPackageManager.Stub {
packages = new String[size];
components = new ArrayList[size];
uids = new int[size];
- Iterator<HashMap.Entry<String, ArrayList<String>>>
+ Iterator<Map.Entry<String, ArrayList<String>>>
it = mPendingBroadcasts.entrySet().iterator();
int i = 0;
while (it.hasNext() && i < size) {
- HashMap.Entry<String, ArrayList<String>> ent = it.next();
+ Map.Entry<String, ArrayList<String>> ent = it.next();
packages[i] = ent.getKey();
components[i] = ent.getValue();
PackageSetting ps = mSettings.mPackages.get(ent.getKey());
@@ -1743,12 +1744,16 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public ActivityInfo getActivityInfo(ComponentName component, int flags) {
+ return getActivityInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
- return PackageParser.generateActivityInfo(a, flags);
+ return PackageParser.generateActivityInfo(a, flags, userId);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
@@ -1758,36 +1763,48 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public ActivityInfo getReceiverInfo(ComponentName component, int flags) {
+ return getReceiverInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
- return PackageParser.generateActivityInfo(a, flags);
+ return PackageParser.generateActivityInfo(a, flags, userId);
}
}
return null;
}
public ServiceInfo getServiceInfo(ComponentName component, int flags) {
+ return getServiceInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
if (s != null && mSettings.isEnabledLPr(s.info, flags)) {
- return PackageParser.generateServiceInfo(s, flags);
+ return PackageParser.generateServiceInfo(s, flags, userId);
}
}
return null;
}
public ProviderInfo getProviderInfo(ComponentName component, int flags) {
+ return getProviderInfo(component, flags, UserId.getUserId(Binder.getCallingUid()));
+ }
+
+ ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Provider p = mProvidersByComponent.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
if (p != null && mSettings.isEnabledLPr(p.info, flags)) {
- return PackageParser.generateProviderInfo(p, flags);
+ return PackageParser.generateProviderInfo(p, flags, userId);
}
}
return null;
@@ -1850,7 +1867,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public int checkUidPermission(String permName, int uid) {
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
if (obj != null) {
GrantedPermissions gp = (GrantedPermissions)obj;
if (gp.grantedPermissions.contains(permName)) {
@@ -1881,7 +1898,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (permName != null) {
BasePermission bp = findPermissionTreeLP(permName);
if (bp != null) {
- if (bp.uid == Binder.getCallingUid()) {
+ if (bp.uid == UserId.getAppId(Binder.getCallingUid())) {
return bp;
}
throw new SecurityException("Calling uid "
@@ -2010,6 +2027,9 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public int checkUidSignatures(int uid1, int uid2) {
+ // Map to base uids.
+ uid1 = UserId.getAppId(uid1);
+ uid2 = UserId.getAppId(uid2);
// reader
synchronized (mPackages) {
Signature[] s1;
@@ -2067,6 +2087,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public String[] getPackagesForUid(int uid) {
+ uid = UserId.getAppId(uid);
// reader
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(uid);
@@ -2091,7 +2112,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public String getNameForUid(int uid) {
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
@@ -2110,7 +2131,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, false);
- if(suid == null) {
+ if (suid == null) {
return -1;
}
return suid.userId;
@@ -2252,6 +2273,9 @@ public class PackageManagerService extends IPackageManager.Stub {
comp = intent.getComponent();
}
}
+
+ final int userId = UserId.getUserId(Binder.getCallingUid());
+
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ActivityInfo ai = getActivityInfo(comp, flags);
@@ -2603,6 +2627,7 @@ public class PackageManagerService extends IPackageManager.Stub {
Arrays.sort(keys);
int i = getContinuationPoint(keys, lastRead);
final int N = keys.length;
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i < N) {
final String packageName = keys[i++];
@@ -2616,7 +2641,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
- ai = PackageParser.generateApplicationInfo(p, flags);
+ ai = PackageParser.generateApplicationInfo(p, flags, userId);
}
}
@@ -2639,12 +2664,13 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
- finalList.add(PackageParser.generateApplicationInfo(p, flags));
+ finalList.add(PackageParser.generateApplicationInfo(p, flags, userId));
}
}
}
@@ -2660,7 +2686,8 @@ public class PackageManagerService extends IPackageManager.Stub {
&& mSettings.isEnabledLPr(provider.info, flags)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
- ? PackageParser.generateProviderInfo(provider, flags)
+ ? PackageParser.generateProviderInfo(provider, flags,
+ UserId.getUserId(Binder.getCallingUid()))
: null;
}
}
@@ -2674,7 +2701,7 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
.iterator();
-
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
PackageParser.Provider p = entry.getValue();
@@ -2683,7 +2710,7 @@ public class PackageManagerService extends IPackageManager.Stub {
&& (!mSafeMode || (p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)) {
outNames.add(entry.getKey());
- outInfo.add(PackageParser.generateProviderInfo(p, 0));
+ outInfo.add(PackageParser.generateProviderInfo(p, 0, userId));
}
}
}
@@ -2696,19 +2723,21 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
if (p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
- && p.info.applicationInfo.uid == uid))
+ && UserId.getAppId(p.info.applicationInfo.uid)
+ == UserId.getAppId(uid)))
&& mSettings.isEnabledLPr(p.info, flags)
&& (!mSafeMode
|| (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
- finalList.add(PackageParser.generateProviderInfo(p, flags));
+ finalList.add(PackageParser.generateProviderInfo(p, flags, userId));
}
}
}
@@ -4461,8 +4490,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.activityInfo = PackageParser.generateActivityInfo(activity,
- mFlags);
+ res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
+ Binder.getOrigCallingUser());
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
@@ -4637,8 +4666,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.serviceInfo = PackageParser.generateServiceInfo(service,
- mFlags);
+ res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
+ Binder.getOrigCallingUser());
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
@@ -4742,8 +4771,10 @@ public class PackageManagerService extends IPackageManager.Stub {
intent.setPackage(targetPkg);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ // TODO: Fix the userId argument
am.broadcastIntent(null, intent, null, finishedReceiver,
- 0, null, null, null, finishedReceiver != null, false);
+ 0, null, null, null, finishedReceiver != null, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException ex) {
}
}
@@ -7584,7 +7615,7 @@ public class PackageManagerService extends IPackageManager.Stub {
"Unknown component: " + packageName
+ "/" + className);
}
- if (!allowedByPermission && (uid != pkgSetting.userId)) {
+ if (!allowedByPermission && (!UserId.isSameApp(uid, pkgSetting.userId))) {
throw new SecurityException(
"Permission Denial: attempt to change component state from pid="
+ Binder.getCallingPid()
@@ -8673,4 +8704,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return mSettings.getVerifierDeviceIdentityLPw();
}
}
+
+ public List<UserInfo> getUsers() {
+ return mUserManager.getUsers();
+ }
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 36442a0..3616aa2 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -62,6 +62,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
import libcore.io.IoUtils;
@@ -713,8 +714,7 @@ final class Settings {
mBackupStoppedPackagesFilename.delete();
FileUtils.setPermissions(mStoppedPackagesFilename.toString(),
FileUtils.S_IRUSR|FileUtils.S_IWUSR
- |FileUtils.S_IRGRP|FileUtils.S_IWGRP
- |FileUtils.S_IROTH,
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP,
-1, -1);
// Done, all is good!
@@ -930,7 +930,7 @@ final class Settings {
}
if (mRenamedPackages.size() > 0) {
- for (HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) {
+ for (Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
serializer.startTag(null, "renamed-package");
serializer.attribute(null, "new", e.getKey());
serializer.attribute(null, "old", e.getValue());
@@ -951,8 +951,7 @@ final class Settings {
mBackupSettingsFilename.delete();
FileUtils.setPermissions(mSettingsFilename.toString(),
FileUtils.S_IRUSR|FileUtils.S_IWUSR
- |FileUtils.S_IRGRP|FileUtils.S_IWGRP
- |FileUtils.S_IROTH,
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP,
-1, -1);
// Write package list file now, use a JournaledFile.
@@ -1007,8 +1006,7 @@ final class Settings {
FileUtils.setPermissions(mPackageListFilename.toString(),
FileUtils.S_IRUSR|FileUtils.S_IWUSR
- |FileUtils.S_IRGRP|FileUtils.S_IWGRP
- |FileUtils.S_IROTH,
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP,
-1, -1);
writeStoppedLPr();
@@ -2147,7 +2145,7 @@ final class Settings {
printedSomething = false;
if (mRenamedPackages.size() > 0) {
- for (final HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) {
+ for (final Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
if (packageName != null && !packageName.equals(e.getKey())
&& !packageName.equals(e.getValue())) {
continue;
@@ -2261,4 +2259,4 @@ final class Settings {
pw.println("Settings parse messages:");
pw.print(mReadMessages.toString());
}
-} \ No newline at end of file
+}
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 76fa5ab..5eacf4a 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -24,6 +24,7 @@ import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.FileUtils;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -140,6 +141,13 @@ public class UserManager {
fallbackToSingleUser();
} catch (XmlPullParserException pe) {
fallbackToSingleUser();
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
+ }
}
}
@@ -162,9 +170,10 @@ public class UserManager {
* </user>
*/
private void writeUser(UserInfo userInfo) {
+ FileOutputStream fos = null;
try {
final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
- final FileOutputStream fos = new FileOutputStream(mUserFile);
+ fos = new FileOutputStream(mUserFile);
final BufferedOutputStream bos = new BufferedOutputStream(fos);
// XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -186,6 +195,13 @@ public class UserManager {
serializer.endDocument();
} catch (IOException ioe) {
Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ioe) {
+ }
+ }
}
}
@@ -198,8 +214,9 @@ public class UserManager {
* </users>
*/
private void writeUserList() {
+ FileOutputStream fos = null;
try {
- final FileOutputStream fos = new FileOutputStream(mUserListFile);
+ fos = new FileOutputStream(mUserListFile);
final BufferedOutputStream bos = new BufferedOutputStream(fos);
// XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -222,6 +239,13 @@ public class UserManager {
serializer.endDocument();
} catch (IOException ioe) {
Slog.e(LOG_TAG, "Error writing user list");
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ioe) {
+ }
+ }
}
}
@@ -265,13 +289,19 @@ public class UserManager {
}
}
}
- fis.close();
UserInfo userInfo = new UserInfo(id, name, flags);
return userInfo;
} catch (IOException ioe) {
} catch (XmlPullParserException pe) {
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
+ }
}
return null;
}
@@ -317,7 +347,7 @@ public class UserManager {
// Don't do it for the primary user, it will become recursive.
if (userId == 0)
continue;
- mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+ mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
userId);
}
}
@@ -354,6 +384,8 @@ public class UserManager {
/**
* Returns the next available user id, filling in any holes in the ids.
+ * TODO: May not be a good idea to recycle ids, in case it results in confusion
+ * for data and battery stats collection, or unexpected cross-talk.
* @return
*/
private int getNextAvailableId() {
@@ -377,14 +409,8 @@ public class UserManager {
FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
| FileUtils.S_IXOTH, -1, -1);
- // Create the individual data directories
- for (ApplicationInfo app : apps) {
- if (app.uid > android.os.Process.FIRST_APPLICATION_UID
- && app.uid < PackageManager.PER_USER_RANGE) {
- mInstaller.createUserData(app.packageName,
- PackageManager.getUid(id, app.uid), id);
- }
- }
+ mInstaller.cloneUserData(0, id, false);
+
final long stopTime = SystemClock.elapsedRealtime();
Log.i(LOG_TAG,
"Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 73cd64e..a19035a 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
import com.android.server.wm.WindowManagerService.H;
import android.content.ClipData;
@@ -28,7 +29,6 @@ import android.os.RemoteException;
import android.util.Slog;
import android.view.DragEvent;
import android.view.InputChannel;
-import android.view.InputQueue;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
@@ -50,6 +50,7 @@ class DragState {
float mCurrentX, mCurrentY;
float mThumbOffsetX, mThumbOffsetY;
InputChannel mServerChannel, mClientChannel;
+ DragInputEventReceiver mInputEventReceiver;
InputApplicationHandle mDragApplicationHandle;
InputWindowHandle mDragWindowHandle;
WindowState mTargetWindow;
@@ -90,8 +91,8 @@ class DragState {
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
- InputQueue.registerInputChannel(mClientChannel, mService.mDragInputHandler,
- mService.mH.getLooper().getQueue());
+ mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
+ mService.mH.getLooper());
mDragApplicationHandle = new InputApplicationHandle(null);
mDragApplicationHandle.name = "drag";
@@ -139,7 +140,8 @@ class DragState {
Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel");
} else {
mService.mInputManager.unregisterInputChannel(mServerChannel);
- InputQueue.unregisterInputChannel(mClientChannel);
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
mClientChannel.dispose();
mServerChannel.dispose();
mClientChannel = null;
diff --git a/services/java/com/android/server/wm/FakeWindowImpl.java b/services/java/com/android/server/wm/FakeWindowImpl.java
index 0e72f7d..121ce18 100644
--- a/services/java/com/android/server/wm/FakeWindowImpl.java
+++ b/services/java/com/android/server/wm/FakeWindowImpl.java
@@ -20,7 +20,7 @@ import android.os.Looper;
import android.os.Process;
import android.util.Slog;
import android.view.InputChannel;
-import android.view.InputHandler;
+import android.view.InputEventReceiver;
import android.view.InputQueue;
import android.view.WindowManagerPolicy;
@@ -29,11 +29,13 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
final InputChannel mServerChannel, mClientChannel;
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
+ final InputEventReceiver mInputEventReceiver;
final int mWindowLayer;
boolean mTouchFullscreen;
- public FakeWindowImpl(WindowManagerService service, Looper looper, InputHandler inputHandler,
+ public FakeWindowImpl(WindowManagerService service,
+ Looper looper, InputEventReceiver.Factory inputEventReceiverFactory,
String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
boolean hasFocus, boolean touchFullscreen) {
mService = service;
@@ -42,7 +44,9 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
- InputQueue.registerInputChannel(mClientChannel, inputHandler, looper.getQueue());
+
+ mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
+ mClientChannel, looper);
mApplicationHandle = new InputApplicationHandle(null);
mApplicationHandle.name = name;
@@ -87,8 +91,8 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
public void dismiss() {
synchronized (mService.mWindowMap) {
if (mService.removeFakeWindowLocked(this)) {
+ mInputEventReceiver.dispose();
mService.mInputManager.unregisterInputChannel(mServerChannel);
- InputQueue.unregisterInputChannel(mClientChannel);
mClientChannel.dispose();
mServerChannel.dispose();
}
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 8fc9a70..55fb038 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -17,13 +17,8 @@
package com.android.server.wm;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.Slog;
import android.view.Surface;
@@ -34,7 +29,8 @@ import android.view.animation.Transformation;
class ScreenRotationAnimation {
static final String TAG = "ScreenRotationAnimation";
- static final boolean DEBUG = false;
+ static final boolean DEBUG_STATE = false;
+ static final boolean DEBUG_TRANSFORMS = false;
static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
@@ -49,11 +45,51 @@ class ScreenRotationAnimation {
int mOriginalWidth, mOriginalHeight;
int mCurRotation;
- Animation mExitAnimation;
+ // For all animations, "exit" is for the UI elements that are going
+ // away (that is the snapshot of the old screen), and "enter" is for
+ // the new UI elements that are appearing (that is the active windows
+ // in their final orientation).
+
+ // The starting animation for the exiting and entering elements. This
+ // animation applies a transformation while the rotation is in progress.
+ // It is started immediately, before the new entering UI is ready.
+ Animation mStartExitAnimation;
+ final Transformation mStartExitTransformation = new Transformation();
+ Animation mStartEnterAnimation;
+ final Transformation mStartEnterTransformation = new Transformation();
+
+ // The finishing animation for the exiting and entering elements. This
+ // animation needs to undo the transformation of the starting animation.
+ // It starts running once the new rotation UI elements are ready to be
+ // displayed.
+ Animation mFinishExitAnimation;
+ final Transformation mFinishExitTransformation = new Transformation();
+ Animation mFinishEnterAnimation;
+ final Transformation mFinishEnterTransformation = new Transformation();
+
+ // The current active animation to move from the old to the new rotated
+ // state. Which animation is run here will depend on the old and new
+ // rotations.
+ Animation mRotateExitAnimation;
+ final Transformation mRotateExitTransformation = new Transformation();
+ Animation mRotateEnterAnimation;
+ final Transformation mRotateEnterTransformation = new Transformation();
+
+ // A previously running rotate animation. This will be used if we need
+ // to switch to a new rotation before finishing the previous one.
+ Animation mLastRotateExitAnimation;
+ final Transformation mLastRotateExitTransformation = new Transformation();
+ Animation mLastRotateEnterAnimation;
+ final Transformation mLastRotateEnterTransformation = new Transformation();
+
+ // Complete transformations being applied.
final Transformation mExitTransformation = new Transformation();
- Animation mEnterAnimation;
final Transformation mEnterTransformation = new Transformation();
+
boolean mStarted;
+ boolean mAnimRunning;
+ boolean mFinishAnimReady;
+ long mFinishAnimStartTime;
final Matrix mSnapshotInitialMatrix = new Matrix();
final Matrix mSnapshotFinalMatrix = new Matrix();
@@ -133,7 +169,7 @@ class ScreenRotationAnimation {
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
mSurface.setAlpha(alpha);
- if (DEBUG) {
+ if (DEBUG_TRANSFORMS) {
float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
float[] dstPnts = new float[4];
matrix.mapPoints(dstPnts, srcPnts);
@@ -167,7 +203,7 @@ class ScreenRotationAnimation {
}
// Must be called while in a transaction.
- public void setRotation(int rotation) {
+ private void setRotation(int rotation) {
mCurRotation = rotation;
// Compute the transformation matrix that must be applied
@@ -176,46 +212,78 @@ class ScreenRotationAnimation {
int delta = deltaRotation(rotation, mSnapshotRotation);
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
- if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta);
+ if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
}
+ // Must be called while in a transaction.
+ public boolean setRotation(int rotation, SurfaceSession session,
+ long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
+ setRotation(rotation);
+ return startAnimation(session, maxAnimationDuration, animationScale,
+ finalWidth, finalHeight, false);
+ }
+
/**
* Returns true if animating.
*/
- public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
- float animationScale, int finalWidth, int finalHeight) {
+ private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+ float animationScale, int finalWidth, int finalHeight, boolean dismissing) {
if (mSurface == null) {
// Can't do animation.
return false;
}
+ if (mStarted) {
+ return true;
+ }
+
+ mStarted = true;
+
+ boolean firstStart = false;
// Figure out how the screen has moved from the original rotation.
int delta = deltaRotation(mCurRotation, mOriginalRotation);
+ if (mFinishExitAnimation == null && (!dismissing || delta != Surface.ROTATION_0)) {
+ if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations");
+ firstStart = true;
+ mStartExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_start_exit);
+ mStartEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_start_enter);
+ mFinishExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_finish_exit);
+ mFinishEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_finish_enter);
+ }
+
+ if (DEBUG_STATE) Slog.v(TAG, "Rotation delta: " + delta + " finalWidth="
+ + finalWidth + " finalHeight=" + finalHeight
+ + " origWidth=" + mOriginalWidth + " origHeight=" + mOriginalHeight);
+
switch (delta) {
case Surface.ROTATION_0:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_enter);
break;
case Surface.ROTATION_90:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_enter);
break;
case Surface.ROTATION_180:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_enter);
break;
case Surface.ROTATION_270:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_enter);
break;
}
@@ -224,35 +292,85 @@ class ScreenRotationAnimation {
// means to allow supplying the last and next size. In this definition
// "%p" is the original (let's call it "previous") size, and "%" is the
// screen's current/new size.
- mEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
- mExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
- mStarted = false;
+ if (firstStart) {
+ if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations");
+ mStartEnterAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mStartExitAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mFinishEnterAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mFinishExitAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ }
+ mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mAnimRunning = false;
+ mFinishAnimReady = false;
+ mFinishAnimStartTime = -1;
+
+ if (firstStart) {
+ mStartExitAnimation.restrictDuration(maxAnimationDuration);
+ mStartExitAnimation.scaleCurrentDuration(animationScale);
+ mStartEnterAnimation.restrictDuration(maxAnimationDuration);
+ mStartEnterAnimation.scaleCurrentDuration(animationScale);
+ mFinishExitAnimation.restrictDuration(maxAnimationDuration);
+ mFinishExitAnimation.scaleCurrentDuration(animationScale);
+ mFinishEnterAnimation.restrictDuration(maxAnimationDuration);
+ mFinishEnterAnimation.scaleCurrentDuration(animationScale);
+ }
+ mRotateExitAnimation.restrictDuration(maxAnimationDuration);
+ mRotateExitAnimation.scaleCurrentDuration(animationScale);
+ mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
+ mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+
+ if (mBlackFrame == null) {
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
+ WindowManagerService.TAG,
+ ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
+ Surface.openTransaction();
- mExitAnimation.restrictDuration(maxAnimationDuration);
- mExitAnimation.scaleCurrentDuration(animationScale);
- mEnterAnimation.restrictDuration(maxAnimationDuration);
- mEnterAnimation.scaleCurrentDuration(animationScale);
+ try {
+ Rect outer = new Rect(-finalWidth*1, -finalHeight*1, finalWidth*2, finalHeight*2);
+ Rect inner = new Rect(0, 0, finalWidth, finalHeight);
+ mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to allocate black surface", e);
+ } finally {
+ Surface.closeTransaction();
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
+ WindowManagerService.TAG,
+ "<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
+ }
+ }
- if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
- ">>> OPEN TRANSACTION ScreenRotationAnimation.dismiss");
- Surface.openTransaction();
+ return true;
+ }
- try {
- Rect outer = new Rect(-finalWidth, -finalHeight, finalWidth * 2, finalHeight * 2);
- Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
- } catch (Surface.OutOfResourcesException e) {
- Slog.w(TAG, "Unable to allocate black surface", e);
- } finally {
- Surface.closeTransaction();
- if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
- "<<< CLOSE TRANSACTION ScreenRotationAnimation.dismiss");
+ /**
+ * Returns true if animating.
+ */
+ public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+ float animationScale, int finalWidth, int finalHeight) {
+ if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
+ if (mSurface == null) {
+ // Can't do animation.
+ return false;
}
-
+ if (!mStarted) {
+ startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+ true);
+ }
+ if (!mStarted) {
+ return false;
+ }
+ if (DEBUG_STATE) Slog.v(TAG, "Setting mFinishAnimReady = true");
+ mFinishAnimReady = true;
return true;
}
public void kill() {
+ if (DEBUG_STATE) Slog.v(TAG, "Kill!");
if (mSurface != null) {
if (WindowManagerService.SHOW_TRANSACTIONS ||
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
@@ -262,74 +380,198 @@ class ScreenRotationAnimation {
}
if (mBlackFrame != null) {
mBlackFrame.kill();
+ mBlackFrame = null;
+ }
+ if (mStartExitAnimation != null) {
+ mStartExitAnimation.cancel();
+ mStartExitAnimation = null;
+ }
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
}
- if (mExitAnimation != null) {
- mExitAnimation.cancel();
- mExitAnimation = null;
+ if (mFinishExitAnimation != null) {
+ mFinishExitAnimation.cancel();
+ mFinishExitAnimation = null;
}
- if (mEnterAnimation != null) {
- mEnterAnimation.cancel();
- mEnterAnimation = null;
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ }
+ if (mRotateExitAnimation != null) {
+ mRotateExitAnimation.cancel();
+ mRotateExitAnimation = null;
+ }
+ if (mRotateEnterAnimation != null) {
+ mRotateEnterAnimation.cancel();
+ mRotateEnterAnimation = null;
}
}
public boolean isAnimating() {
- return mEnterAnimation != null || mExitAnimation != null;
+ return mStartEnterAnimation != null || mStartExitAnimation != null
+ && mFinishEnterAnimation != null || mFinishExitAnimation != null
+ && mRotateEnterAnimation != null || mRotateExitAnimation != null;
}
public boolean stepAnimation(long now) {
- if (mEnterAnimation == null && mExitAnimation == null) {
+ if (!isAnimating()) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
return false;
}
- if (!mStarted) {
- if (mEnterAnimation != null) {
- mEnterAnimation.setStartTime(now);
+ if (!mAnimRunning) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: starting start, finish, rotate");
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.setStartTime(now);
}
- if (mExitAnimation != null) {
- mExitAnimation.setStartTime(now);
+ if (mStartExitAnimation != null) {
+ mStartExitAnimation.setStartTime(now);
}
- mStarted = true;
- }
-
- mExitTransformation.clear();
- boolean moreExit = false;
- if (mExitAnimation != null) {
- moreExit = mExitAnimation.getTransformation(now, mExitTransformation);
- if (DEBUG) Slog.v(TAG, "Stepped exit: " + mExitTransformation);
- if (!moreExit) {
- if (DEBUG) Slog.v(TAG, "Exit animation done!");
- mExitAnimation.cancel();
- mExitAnimation = null;
- mExitTransformation.clear();
- if (mSurface != null) {
- mSurface.hide();
- }
+ if (mFinishEnterAnimation != null) {
+ mFinishEnterAnimation.setStartTime(0);
+ }
+ if (mFinishExitAnimation != null) {
+ mFinishExitAnimation.setStartTime(0);
+ }
+ if (mRotateEnterAnimation != null) {
+ mRotateEnterAnimation.setStartTime(now);
}
+ if (mRotateExitAnimation != null) {
+ mRotateExitAnimation.setStartTime(now);
+ }
+ mAnimRunning = true;
}
- mEnterTransformation.clear();
- boolean moreEnter = false;
- if (mEnterAnimation != null) {
- moreEnter = mEnterAnimation.getTransformation(now, mEnterTransformation);
- if (!moreEnter) {
- mEnterAnimation.cancel();
- mEnterAnimation = null;
- mEnterTransformation.clear();
- if (mBlackFrame != null) {
- mBlackFrame.hide();
- }
- } else {
- if (mBlackFrame != null) {
- mBlackFrame.setMatrix(mEnterTransformation.getMatrix());
- }
+ if (mFinishAnimReady && mFinishAnimStartTime < 0) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready");
+ mFinishAnimStartTime = now;
+ }
+
+ // If the start animation is no longer running, we want to keep its
+ // transformation intact until the finish animation also completes.
+
+ boolean moreStartExit = false;
+ if (mStartExitAnimation != null) {
+ mStartExitTransformation.clear();
+ moreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation);
+ if (!moreStartExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Start exit animation done!");
+ mStartExitAnimation.cancel();
+ mStartExitAnimation = null;
+ }
+ }
+
+ boolean moreStartEnter = false;
+ if (mStartEnterAnimation != null) {
+ mStartEnterTransformation.clear();
+ moreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation);
+ if (!moreStartEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Start enter animation done!");
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ }
+ }
+
+ long finishNow = mFinishAnimReady ? (now - mFinishAnimStartTime) : 0;
+ if (DEBUG_STATE) Slog.v(TAG, "Step: finishNow=" + finishNow);
+
+ mFinishExitTransformation.clear();
+ boolean moreFinishExit = false;
+ if (mFinishExitAnimation != null) {
+ moreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation);
+ if (!moreStartExit && !moreFinishExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Finish exit animation done, clearing start/finish anims!");
+ mStartExitTransformation.clear();
+ mFinishExitAnimation.cancel();
+ mFinishExitAnimation = null;
+ mFinishExitTransformation.clear();
+ }
+ }
+
+ mFinishEnterTransformation.clear();
+ boolean moreFinishEnter = false;
+ if (mFinishEnterAnimation != null) {
+ moreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation);
+ if (!moreStartEnter && !moreFinishEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Finish enter animation done, clearing start/finish anims!");
+ mStartEnterTransformation.clear();
+ mFinishEnterAnimation.cancel();
+ mFinishEnterAnimation = null;
+ mFinishEnterTransformation.clear();
+ }
+ }
+
+ mRotateExitTransformation.clear();
+ boolean moreRotateExit = false;
+ if (mRotateExitAnimation != null) {
+ moreRotateExit = mRotateExitAnimation.getTransformation(now, mRotateExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate exit: " + mRotateExitTransformation);
+ }
+
+ if (!moreFinishExit && !moreRotateExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Rotate exit animation done!");
+ mRotateExitAnimation.cancel();
+ mRotateExitAnimation = null;
+ mRotateExitTransformation.clear();
+ }
+
+ mRotateEnterTransformation.clear();
+ boolean moreRotateEnter = false;
+ if (mRotateEnterAnimation != null) {
+ moreRotateEnter = mRotateEnterAnimation.getTransformation(now, mRotateEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate enter: " + mRotateEnterTransformation);
+ }
+
+ if (!moreFinishEnter && !moreRotateEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Rotate enter animation done!");
+ mRotateEnterAnimation.cancel();
+ mRotateEnterAnimation = null;
+ mRotateEnterTransformation.clear();
+ }
+
+ mExitTransformation.set(mRotateExitTransformation);
+ mExitTransformation.compose(mStartExitTransformation);
+ mExitTransformation.compose(mFinishExitTransformation);
+
+ mEnterTransformation.set(mRotateEnterTransformation);
+ mEnterTransformation.compose(mStartEnterTransformation);
+ mEnterTransformation.compose(mFinishEnterTransformation);
+
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final exit: " + mExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final enter: " + mEnterTransformation);
+
+ if (!moreStartExit && !moreFinishExit && !moreRotateExit) {
+ if (mSurface != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, hiding screenshot surface");
+ mSurface.hide();
+ }
+ }
+
+ if (!moreStartEnter && !moreFinishEnter && !moreRotateEnter) {
+ if (mBlackFrame != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, hiding black frame");
+ mBlackFrame.hide();
+ }
+ } else {
+ if (mBlackFrame != null) {
+ mBlackFrame.setMatrix(mEnterTransformation.getMatrix());
}
}
mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
- return moreEnter || moreExit;
+ final boolean more = moreStartEnter || moreStartExit || moreFinishEnter || moreFinishExit
+ || moreRotateEnter || moreRotateExit || !mFinishAnimReady;
+
+ if (DEBUG_STATE) Slog.v(TAG, "Step: more=" + more);
+
+ return more;
}
public Transformation getEnterTransformation() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index bd33e0c..620d74c 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -36,7 +36,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
import com.android.internal.policy.impl.PhoneWindowManager;
-import com.android.internal.view.BaseInputHandler;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
@@ -96,6 +95,7 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TypedValue;
+import android.view.Choreographer;
import android.view.Display;
import android.view.Gravity;
import android.view.IApplicationToken;
@@ -107,8 +107,7 @@ import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.InputHandler;
-import android.view.InputQueue;
+import android.view.InputEventReceiver;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
@@ -143,7 +142,8 @@ import java.util.List;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
+ implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
+ Choreographer.OnAnimateListener {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
@@ -458,7 +458,7 @@ public class WindowManagerService extends IWindowManager.Stub
int mDeferredRotationPauseCount;
boolean mLayoutNeeded = true;
- boolean mAnimationPending = false;
+ boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
boolean mWaitingForConfig = false;
boolean mWindowsFreezingScreen = false;
@@ -505,7 +505,9 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics();
- H mH = new H();
+ final H mH = new H();
+
+ final Choreographer mChoreographer = Choreographer.getInstance();
WindowState mCurrentFocus = null;
WindowState mLastFocus = null;
@@ -561,6 +563,7 @@ public class WindowManagerService extends IWindowManager.Stub
float mWindowAnimationScale = 1.0f;
float mTransitionAnimationScale = 1.0f;
+ float mAnimatorDurationScale = 1.0f;
final InputManager mInputManager;
@@ -571,18 +574,25 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mTurnOnScreen;
DragState mDragState = null;
- final InputHandler mDragInputHandler = new BaseInputHandler() {
+
+ final class DragInputEventReceiver extends InputEventReceiver {
+ public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
+ if (event instanceof MotionEvent
+ && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
&& mDragState != null) {
+ final MotionEvent motionEvent = (MotionEvent)event;
boolean endDrag = false;
- final float newX = event.getRawX();
- final float newY = event.getRawY();
+ final float newX = motionEvent.getRawX();
+ final float newY = motionEvent.getRawY();
- switch (event.getAction()) {
+ switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (DEBUG_DRAG) {
Slog.w(TAG, "Unexpected ACTION_DOWN in drag layer");
@@ -623,10 +633,10 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (Exception e) {
Slog.e(TAG, "Exception caught by drag handleMotion", e);
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
- };
+ }
/**
* Whether the UI is currently running in touch mode (not showing
@@ -686,6 +696,7 @@ public class WindowManagerService extends IWindowManager.Stub
Looper.prepare();
WindowManagerService s = new WindowManagerService(mContext, mPM,
mHaveInputMethods, mAllowBootMessages);
+ s.mChoreographer.addOnAnimateListener(s);
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_DISPLAY);
android.os.Process.setCanSelfBackground(false);
@@ -769,6 +780,8 @@ public class WindowManagerService extends IWindowManager.Stub
Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+ mAnimatorDurationScale = Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.ANIMATOR_DURATION_SCALE, mTransitionAnimationScale);
// Track changes to DevicePolicyManager state so we can enable/disable keyguard.
IntentFilter filter = new IntentFilter();
@@ -3448,7 +3461,7 @@ public class WindowManagerService extends IWindowManager.Stub
// the value of the previous configuration.
mTempConfiguration.setToDefaults();
mTempConfiguration.fontScale = currentConfig.fontScale;
- if (computeNewConfigurationLocked(mTempConfiguration)) {
+ if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
mLayoutNeeded = true;
@@ -4652,6 +4665,7 @@ public class WindowManagerService extends IWindowManager.Stub
switch (which) {
case 0: mWindowAnimationScale = fixScale(scale); break;
case 1: mTransitionAnimationScale = fixScale(scale); break;
+ case 2: mAnimatorDurationScale = fixScale(scale); break;
}
// Persist setting
@@ -4671,6 +4685,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (scales.length >= 2) {
mTransitionAnimationScale = fixScale(scales[1]);
}
+ if (scales.length >= 3) {
+ mAnimatorDurationScale = fixScale(scales[2]);
+ }
}
// Persist setting
@@ -4681,12 +4698,14 @@ public class WindowManagerService extends IWindowManager.Stub
switch (which) {
case 0: return mWindowAnimationScale;
case 1: return mTransitionAnimationScale;
+ case 2: return mAnimatorDurationScale;
}
return 0;
}
public float[] getAnimationScales() {
- return new float[] { mWindowAnimationScale, mTransitionAnimationScale };
+ return new float[] { mWindowAnimationScale, mTransitionAnimationScale,
+ mAnimatorDurationScale };
}
public int getSwitchState(int sw) {
@@ -5364,6 +5383,14 @@ public class WindowManagerService extends IWindowManager.Stub
startFreezingDisplayLocked(inTransaction);
mInputManager.setDisplayOrientation(0, rotation);
+ // We need to update our screen size information to match the new
+ // rotation. Note that this is redundant with the later call to
+ // sendNewConfiguration() that must be called after this function
+ // returns... however we need to do the screen size part of that
+ // before then so we have the correct size to use when initializiation
+ // the rotation animation for the new rotation.
+ computeScreenConfigurationLocked(null);
+
if (!inTransaction) {
if (SHOW_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION setRotationUnchecked");
@@ -5374,7 +5401,11 @@ public class WindowManagerService extends IWindowManager.Stub
// it doesn't support hardware OpenGL emulation yet.
if (CUSTOM_SCREEN_ROTATION && mScreenRotationAnimation != null
&& mScreenRotationAnimation.hasScreenshot()) {
- mScreenRotationAnimation.setRotation(rotation);
+ if (mScreenRotationAnimation.setRotation(rotation, mFxSession,
+ MAX_ANIMATION_DURATION, mTransitionAnimationScale,
+ mCurDisplayWidth, mCurDisplayHeight)) {
+ mChoreographer.scheduleAnimation();
+ }
}
Surface.setOrientation(0, rotation);
} finally {
@@ -5862,7 +5893,7 @@ public class WindowManagerService extends IWindowManager.Stub
Configuration computeNewConfigurationLocked() {
Configuration config = new Configuration();
config.fontScale = 0;
- if (!computeNewConfigurationLocked(config)) {
+ if (!computeScreenConfigurationLocked(config)) {
return null;
}
return config;
@@ -6013,12 +6044,10 @@ public class WindowManagerService extends IWindowManager.Stub
return sw;
}
- boolean computeNewConfigurationLocked(Configuration config) {
+ boolean computeScreenConfigurationLocked(Configuration config) {
if (mDisplay == null) {
return false;
}
-
- mInputManager.getInputConfiguration(config);
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -6052,13 +6081,17 @@ public class WindowManagerService extends IWindowManager.Stub
final int dw = mCurDisplayWidth;
final int dh = mCurDisplayHeight;
- int orientation = Configuration.ORIENTATION_SQUARE;
- if (dw < dh) {
- orientation = Configuration.ORIENTATION_PORTRAIT;
- } else if (dw > dh) {
- orientation = Configuration.ORIENTATION_LANDSCAPE;
+ if (config != null) {
+ mInputManager.getInputConfiguration(config);
+
+ int orientation = Configuration.ORIENTATION_SQUARE;
+ if (dw < dh) {
+ orientation = Configuration.ORIENTATION_PORTRAIT;
+ } else if (dw > dh) {
+ orientation = Configuration.ORIENTATION_LANDSCAPE;
+ }
+ config.orientation = orientation;
}
- config.orientation = orientation;
// Update real display metrics.
mDisplay.getMetricsWithSize(mRealDisplayMetrics, mCurDisplayWidth, mCurDisplayHeight);
@@ -6080,36 +6113,39 @@ public class WindowManagerService extends IWindowManager.Stub
mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm,
mCompatDisplayMetrics);
- config.screenWidthDp = (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation)
- / dm.density);
- config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation)
- / dm.density);
- computeSmallestWidthAndScreenLayout(rotated, dw, dh, dm.density, config);
+ if (config != null) {
+ config.screenWidthDp = (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation)
+ / dm.density);
+ config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation)
+ / 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);
+ config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
+ config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
+ config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh);
- // Determine whether a hard keyboard is available and enabled.
- boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
- if (hardKeyboardAvailable != mHardKeyboardAvailable) {
- mHardKeyboardAvailable = hardKeyboardAvailable;
- mHardKeyboardEnabled = hardKeyboardAvailable;
+ // Determine whether a hard keyboard is available and enabled.
+ boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
+ if (hardKeyboardAvailable != mHardKeyboardAvailable) {
+ mHardKeyboardAvailable = hardKeyboardAvailable;
+ mHardKeyboardEnabled = hardKeyboardAvailable;
- mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
- mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
- }
- if (!mHardKeyboardEnabled) {
- config.keyboard = Configuration.KEYBOARD_NOKEYS;
+ mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+ mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+ }
+ if (!mHardKeyboardEnabled) {
+ config.keyboard = Configuration.KEYBOARD_NOKEYS;
+ }
+
+ // Update value of keyboardHidden, hardKeyboardHidden and navigationHidden
+ // based on whether a hard or soft keyboard is present, whether navigation keys
+ // are present and the lid switch state.
+ config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
+ config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
+ config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
+ mPolicy.adjustConfigurationLw(config);
}
- // Update value of keyboardHidden, hardKeyboardHidden and navigationHidden
- // based on whether a hard or soft keyboard is present, whether navigation keys
- // are present and the lid switch state.
- config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
- config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
- config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
- mPolicy.adjustConfigurationLw(config);
return true;
}
@@ -6491,7 +6527,7 @@ public class WindowManagerService extends IWindowManager.Stub
final class H extends Handler {
public static final int REPORT_FOCUS_CHANGE = 2;
public static final int REPORT_LOSING_FOCUS = 3;
- public static final int ANIMATE = 4;
+ public static final int DO_TRAVERSAL = 4;
public static final int ADD_STARTING = 5;
public static final int REMOVE_STARTING = 6;
public static final int FINISHED_STARTING = 7;
@@ -6585,9 +6621,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
} break;
- case ANIMATE: {
+ case DO_TRAVERSAL: {
synchronized(mWindowMap) {
- mAnimationPending = false;
+ mTraversalScheduled = false;
performLayoutAndPlaceSurfacesLocked();
}
} break;
@@ -6803,12 +6839,14 @@ public class WindowManagerService extends IWindowManager.Stub
Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale);
break;
}
case FORCE_GC: {
synchronized(mWindowMap) {
- if (mAnimationPending) {
+ if (mChoreographer.isAnimationScheduled()) {
// If we are animating, don't do the gc now but
// delay a bit so we don't interrupt the animation.
mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
@@ -7116,7 +7154,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean configChanged = updateOrientationFromAppTokensLocked(false);
mTempConfiguration.setToDefaults();
mTempConfiguration.fontScale = mCurConfiguration.fontScale;
- if (computeNewConfigurationLocked(mTempConfiguration)) {
+ if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (mCurConfiguration.diff(mTempConfiguration) != 0) {
configChanged = true;
}
@@ -7367,7 +7405,7 @@ public class WindowManagerService extends IWindowManager.Stub
} else {
mInLayout = false;
if (mLayoutNeeded) {
- requestAnimationLocked(0);
+ requestTraversalLocked();
}
}
if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
@@ -8800,10 +8838,9 @@ public class WindowManagerService extends IWindowManager.Stub
needRelayout = adjustWallpaperWindowsLocked() != 0;
}
if (needRelayout) {
- requestAnimationLocked(0);
+ requestTraversalLocked();
} else if (animating) {
- final int refreshTimeUs = (int)(1000 / mDisplay.getRefreshRate());
- requestAnimationLocked(currentTime + refreshTimeUs - SystemClock.uptimeMillis());
+ mChoreographer.scheduleAnimation();
}
// Finally update all input windows now that the window changes have stabilized.
@@ -8922,10 +8959,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void requestAnimationLocked(long delay) {
- if (!mAnimationPending) {
- mAnimationPending = true;
- mH.sendMessageDelayed(mH.obtainMessage(H.ANIMATE), delay);
+ void requestTraversalLocked() {
+ if (!mTraversalScheduled) {
+ mTraversalScheduled = true;
+ mH.sendEmptyMessage(H.DO_TRAVERSAL);
+ }
+ }
+
+ @Override
+ public void onAnimate() {
+ synchronized(mWindowMap) {
+ performLayoutAndPlaceSurfacesLocked();
}
}
@@ -9245,7 +9289,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation");
if (mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) {
- requestAnimationLocked(0);
+ mChoreographer.scheduleAnimation();
} else {
mScreenRotationAnimation = null;
updateRotation = true;
@@ -9391,11 +9435,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public FakeWindow addFakeWindow(Looper looper, InputHandler inputHandler,
+ public FakeWindow addFakeWindow(Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory,
String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
boolean hasFocus, boolean touchFullscreen) {
synchronized (mWindowMap) {
- FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputHandler, name, windowType,
+ FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputEventReceiverFactory,
+ name, windowType,
layoutParamsFlags, canReceiveKeys, hasFocus, touchFullscreen);
int i=0;
while (i<mFakeWindows.size()) {
@@ -9735,9 +9781,10 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mLastWindowForcedOrientation"); pw.print(mLastWindowForcedOrientation);
pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
- pw.print(" mAnimationPending="); pw.print(mAnimationPending);
+ pw.print(" mTraversalScheduled="); pw.print(mTraversalScheduled);
pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale);
pw.print(" mTransitionWindowAnimationScale="); pw.println(mTransitionAnimationScale);
+ pw.print(" mAnimatorDurationScale="); pw.println(mAnimatorDurationScale);
pw.print(" mNextAppTransition=0x");
pw.print(Integer.toHexString(mNextAppTransition));
pw.print(" mAppTransitionReady="); pw.println(mAppTransitionReady);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 1067cad..6868cf6 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -1593,7 +1593,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
}
if (requestAnim) {
- mService.requestAnimationLocked(0);
+ mService.mChoreographer.scheduleAnimation();
}
return true;
}
@@ -1634,7 +1634,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
}
if (requestAnim) {
- mService.requestAnimationLocked(0);
+ mService.mChoreographer.scheduleAnimation();
}
return true;
}
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index 5005864..d2b3118 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -28,6 +28,8 @@
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+
#include "com_android_server_PowerManagerService.h"
namespace android {
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 8659025..dd6c426 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -522,7 +522,7 @@ bool SensorService::SensorRecord::removeConnection(
SensorService::SensorEventConnection::SensorEventConnection(
const sp<SensorService>& service)
- : mService(service), mChannel(new SensorChannel())
+ : mService(service), mChannel(new BitTube())
{
}
@@ -604,7 +604,7 @@ status_t SensorService::SensorEventConnection::sendEvents(
return size < 0 ? status_t(size) : status_t(NO_ERROR);
}
-sp<SensorChannel> SensorService::SensorEventConnection::getSensorChannel() const
+sp<BitTube> SensorService::SensorEventConnection::getSensorChannel() const
{
return mChannel;
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 85f4ecb..e357f96 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -29,7 +29,7 @@
#include <binder/BinderService.h>
#include <gui/Sensor.h>
-#include <gui/SensorChannel.h>
+#include <gui/BitTube.h>
#include <gui/ISensorServer.h>
#include <gui/ISensorEventConnection.h>
@@ -71,12 +71,12 @@ class SensorService :
class SensorEventConnection : public BnSensorEventConnection {
virtual ~SensorEventConnection();
virtual void onFirstRef();
- virtual sp<SensorChannel> getSensorChannel() const;
+ virtual sp<BitTube> getSensorChannel() const;
virtual status_t enableDisable(int handle, bool enabled);
virtual status_t setEventRate(int handle, nsecs_t ns);
sp<SensorService> const mService;
- sp<SensorChannel> const mChannel;
+ sp<BitTube> const mChannel;
mutable Mutex mConnectionLock;
// protected by SensorService::mLock
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index f63c0c1..42e280f 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -2,36 +2,42 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- Layer.cpp \
- LayerBase.cpp \
- LayerDim.cpp \
- LayerScreenshot.cpp \
- DdmConnection.cpp \
- DisplayHardware/DisplayHardware.cpp \
+ EventThread.cpp \
+ Layer.cpp \
+ LayerBase.cpp \
+ LayerDim.cpp \
+ LayerScreenshot.cpp \
+ DdmConnection.cpp \
+ DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
- DisplayHardware/HWComposer.cpp \
- GLExtensions.cpp \
- MessageQueue.cpp \
- SurfaceFlinger.cpp \
- SurfaceTextureLayer.cpp \
- Transform.cpp \
+ DisplayHardware/HWComposer.cpp \
+ DisplayHardware/VSyncBarrier.cpp \
+ DisplayEventConnection.cpp \
+ GLExtensions.cpp \
+ MessageQueue.cpp \
+ SurfaceFlinger.cpp \
+ SurfaceTextureLayer.cpp \
+ Transform.cpp \
LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+ifeq ($(TARGET_HAS_WAITFORVSYNC), true)
+ LOCAL_CFLAGS += -DHAS_WAITFORVSYNC
+endif
+
ifeq ($(TARGET_BOARD_PLATFORM), omap3)
LOCAL_CFLAGS += -DNO_RGBX_8888
endif
ifeq ($(TARGET_BOARD_PLATFORM), omap4)
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
+ LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING
endif
ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
- LOCAL_CFLAGS += -DREFRESH_RATE=56
endif
-
LOCAL_SHARED_LIBRARIES := \
libcutils \
libhardware \
diff --git a/services/surfaceflinger/DisplayEventConnection.cpp b/services/surfaceflinger/DisplayEventConnection.cpp
new file mode 100644
index 0000000..77ecbd2
--- /dev/null
+++ b/services/surfaceflinger/DisplayEventConnection.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/BitTube.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+
+#include "SurfaceFlinger.h"
+#include "DisplayEventConnection.h"
+#include "EventThread.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+DisplayEventConnection::DisplayEventConnection(
+ const sp<EventThread>& eventThread)
+ : mEventThread(eventThread), mChannel(new BitTube())
+{
+}
+
+DisplayEventConnection::~DisplayEventConnection() {
+ mEventThread->unregisterDisplayEventConnection(this);
+}
+
+void DisplayEventConnection::onFirstRef() {
+ // NOTE: mEventThread doesn't hold a strong reference on us
+ mEventThread->registerDisplayEventConnection(this);
+}
+
+sp<BitTube> DisplayEventConnection::getDataChannel() const {
+ return mChannel;
+}
+
+void DisplayEventConnection::setVsyncRate(uint32_t count) {
+ mEventThread->setVsyncRate(count, this);
+}
+
+void DisplayEventConnection::requestNextVsync() {
+ mEventThread->requestNextVsync(this);
+}
+
+status_t DisplayEventConnection::postEvent(const DisplayEventReceiver::Event& event)
+{
+ ssize_t size = mChannel->write(&event, sizeof(DisplayEventReceiver::Event));
+ return size < 0 ? status_t(size) : status_t(NO_ERROR);
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayEventConnection.h b/services/surfaceflinger/DisplayEventConnection.h
new file mode 100644
index 0000000..cc3ee36
--- /dev/null
+++ b/services/surfaceflinger/DisplayEventConnection.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H
+#define ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+
+#include <utils/Errors.h>
+#include <gui/DisplayEventReceiver.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class BitTube;
+class EventThread;
+
+// ---------------------------------------------------------------------------
+
+class DisplayEventConnection : public BnDisplayEventConnection {
+public:
+ DisplayEventConnection(const sp<EventThread>& flinger);
+
+ status_t postEvent(const DisplayEventReceiver::Event& event);
+
+private:
+ virtual ~DisplayEventConnection();
+ virtual void onFirstRef();
+ virtual sp<BitTube> getDataChannel() const;
+ virtual void setVsyncRate(uint32_t count);
+ virtual void requestNextVsync(); // asynchronous
+
+ sp<EventThread> const mEventThread;
+ sp<BitTube> const mChannel;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif /* ANDROID_SURFACE_FLINGER_DISPLAY_EVENT_CONNECTION_H */
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 61096e5..986aec5 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -140,6 +140,7 @@ void DisplayHardware::init(uint32_t dpy)
mDpiX = mNativeWindow->xdpi;
mDpiY = mNativeWindow->ydpi;
mRefreshRate = fbDev->fps;
+ mNextFakeVSync = 0;
/* FIXME: this is a temporary HACK until we are able to report the refresh rate
@@ -152,6 +153,8 @@ void DisplayHardware::init(uint32_t dpy)
#warning "refresh rate set via makefile to REFRESH_RATE"
#endif
+ mRefreshPeriod = nsecs_t(1e9 / mRefreshRate);
+
EGLint w, h, dummy;
EGLint numConfigs=0;
EGLSurface surface;
@@ -346,12 +349,52 @@ uint32_t DisplayHardware::getPageFlipCount() const {
return mPageFlipCount;
}
-status_t DisplayHardware::compositionComplete() const {
- return mNativeWindow->compositionComplete();
+// this needs to be thread safe
+nsecs_t DisplayHardware::waitForRefresh() const {
+ nsecs_t timestamp;
+ if (mVSync.wait(&timestamp) < 0) {
+ // vsync not supported!
+ usleep( getDelayToNextVSyncUs(&timestamp) );
+ }
+ mLastHwVSync = timestamp; // FIXME: Not thread safe
+ return timestamp;
+}
+
+nsecs_t DisplayHardware::getRefreshTimestamp() const {
+ // this returns the last refresh timestamp.
+ // if the last one is not available, we estimate it based on
+ // the refresh period and whatever closest timestamp we have.
+ nsecs_t now = systemTime();
+ return now - ((now - mLastHwVSync) % mRefreshPeriod);
+}
+
+nsecs_t DisplayHardware::getRefreshPeriod() const {
+ return mRefreshPeriod;
}
-int DisplayHardware::getCurrentBufferIndex() const {
- return mNativeWindow->getCurrentBufferIndex();
+int32_t DisplayHardware::getDelayToNextVSyncUs(nsecs_t* timestamp) const {
+ Mutex::Autolock _l(mFakeVSyncMutex);
+ const nsecs_t period = mRefreshPeriod;
+ const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ nsecs_t next_vsync = mNextFakeVSync;
+ nsecs_t sleep = next_vsync - now;
+ if (sleep < 0) {
+ // we missed, find where the next vsync should be
+ sleep = (period - ((now - next_vsync) % period));
+ next_vsync = now + sleep;
+ }
+ mNextFakeVSync = next_vsync + period;
+ timestamp[0] = next_vsync;
+
+ // round to next microsecond
+ int32_t sleep_us = (sleep + 999LL) / 1000LL;
+
+ // guaranteed to be > 0
+ return sleep_us;
+}
+
+status_t DisplayHardware::compositionComplete() const {
+ return mNativeWindow->compositionComplete();
}
void DisplayHardware::flip(const Region& dirty) const
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index f02c954..02be4dc 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -32,6 +32,7 @@
#include "GLExtensions.h"
#include "DisplayHardware/DisplayHardwareBase.h"
+#include "DisplayHardware/VSyncBarrier.h"
namespace android {
@@ -74,6 +75,11 @@ public:
uint32_t getMaxTextureSize() const;
uint32_t getMaxViewportDims() const;
+ // waits for the next vsync and returns the timestamp of when it happened
+ nsecs_t waitForRefresh() const;
+ nsecs_t getRefreshPeriod() const;
+ nsecs_t getRefreshTimestamp() const;
+
uint32_t getPageFlipCount() const;
EGLDisplay getEGLDisplay() const { return mDisplay; }
@@ -89,12 +95,10 @@ public:
}
inline Rect bounds() const { return getBounds(); }
- // only for debugging
- int getCurrentBufferIndex() const;
-
private:
void init(uint32_t displayIndex) __attribute__((noinline));
void fini() __attribute__((noinline));
+ int32_t getDelayToNextVSyncUs(nsecs_t* timestamp) const;
sp<SurfaceFlinger> mFlinger;
EGLDisplay mDisplay;
@@ -112,7 +116,13 @@ private:
mutable uint32_t mPageFlipCount;
GLint mMaxViewportDims[2];
GLint mMaxTextureSize;
-
+ VSyncBarrier mVSync;
+
+ mutable Mutex mFakeVSyncMutex;
+ mutable nsecs_t mNextFakeVSync;
+ nsecs_t mRefreshPeriod;
+ mutable nsecs_t mLastHwVSync;
+
HWComposer* mHwc;
sp<FramebufferNativeWindow> mNativeWindow;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index f4afeea..69f1aca 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2012 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.
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
@@ -22,15 +21,6 @@
#include <unistd.h>
#include <fcntl.h>
-#include <signal.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-
-#include <linux/unistd.h>
#include <utils/Log.h>
@@ -45,39 +35,22 @@ static char const * const kWakeFileName = "/sys/power/wait_for_fb_wake";
// ----------------------------------------------------------------------------
-DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase(
+DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
const sp<SurfaceFlinger>& flinger)
: Thread(false), mFlinger(flinger) {
}
-DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() {
+DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() {
}
-// ----------------------------------------------------------------------------
-
-DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
- const sp<SurfaceFlinger>& flinger)
- : DisplayEventThreadBase(flinger)
-{
+status_t DisplayHardwareBase::DisplayEventThread::initCheck() const {
+ return ((access(kSleepFileName, R_OK) == 0 &&
+ access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT;
}
-DisplayHardwareBase::DisplayEventThread::~DisplayEventThread()
-{
-}
+bool DisplayHardwareBase::DisplayEventThread::threadLoop() {
-bool DisplayHardwareBase::DisplayEventThread::threadLoop()
-{
- int err = 0;
- char buf;
- int fd;
-
- fd = open(kSleepFileName, O_RDONLY, 0);
- do {
- err = read(fd, &buf, 1);
- } while (err < 0 && errno == EINTR);
- close(fd);
- ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
- if (err >= 0) {
+ if (waitForFbSleep() == NO_ERROR) {
sp<SurfaceFlinger> flinger = mFlinger.promote();
ALOGD("About to give-up screen, flinger = %p", flinger.get());
if (flinger != 0) {
@@ -85,39 +58,51 @@ bool DisplayHardwareBase::DisplayEventThread::threadLoop()
flinger->screenReleased(0);
mBarrier.wait();
}
+ if (waitForFbWake() == NO_ERROR) {
+ sp<SurfaceFlinger> flinger = mFlinger.promote();
+ ALOGD("Screen about to return, flinger = %p", flinger.get());
+ if (flinger != 0) {
+ flinger->screenAcquired(0);
+ }
+ return true;
+ }
}
- fd = open(kWakeFileName, O_RDONLY, 0);
+
+ // error, exit the thread
+ return false;
+}
+
+status_t DisplayHardwareBase::DisplayEventThread::waitForFbSleep() {
+ int err = 0;
+ char buf;
+ int fd = open(kSleepFileName, O_RDONLY, 0);
+ // if the file doesn't exist, the error will be caught in read() below
do {
- err = read(fd, &buf, 1);
+ err = read(fd, &buf, 1);
} while (err < 0 && errno == EINTR);
close(fd);
- ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
- if (err >= 0) {
- sp<SurfaceFlinger> flinger = mFlinger.promote();
- ALOGD("Screen about to return, flinger = %p", flinger.get());
- if (flinger != 0)
- flinger->screenAcquired(0);
- }
- return true;
+ ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
+ return err < 0 ? -errno : int(NO_ERROR);
}
-status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const
-{
- mBarrier.open();
- return NO_ERROR;
+status_t DisplayHardwareBase::DisplayEventThread::waitForFbWake() {
+ int err = 0;
+ char buf;
+ int fd = open(kWakeFileName, O_RDONLY, 0);
+ // if the file doesn't exist, the error will be caught in read() below
+ do {
+ err = read(fd, &buf, 1);
+ } while (err < 0 && errno == EINTR);
+ close(fd);
+ ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
+ return err < 0 ? -errno : int(NO_ERROR);
}
-status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
-{
+status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const {
+ mBarrier.open();
return NO_ERROR;
}
-status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
-{
- return ((access(kSleepFileName, R_OK) == 0 &&
- access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT;
-}
-
// ----------------------------------------------------------------------------
DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
@@ -127,35 +112,35 @@ DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
mDisplayEventThread = new DisplayEventThread(flinger);
}
-DisplayHardwareBase::~DisplayHardwareBase()
-{
+void DisplayHardwareBase::startSleepManagement() const {
+ if (mDisplayEventThread->initCheck() == NO_ERROR) {
+ mDisplayEventThread->run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
+ } else {
+ ALOGW("/sys/power/wait_for_fb_{wake|sleep} don't exist");
+ }
+}
+
+DisplayHardwareBase::~DisplayHardwareBase() {
// request exit
mDisplayEventThread->requestExitAndWait();
}
-bool DisplayHardwareBase::canDraw() const
-{
+bool DisplayHardwareBase::canDraw() const {
return mScreenAcquired;
}
-void DisplayHardwareBase::releaseScreen() const
-{
+void DisplayHardwareBase::releaseScreen() const {
status_t err = mDisplayEventThread->releaseScreen();
if (err >= 0) {
mScreenAcquired = false;
}
}
-void DisplayHardwareBase::acquireScreen() const
-{
- status_t err = mDisplayEventThread->acquireScreen();
- if (err >= 0) {
- mScreenAcquired = true;
- }
+void DisplayHardwareBase::acquireScreen() const {
+ mScreenAcquired = true;
}
-bool DisplayHardwareBase::isScreenAcquired() const
-{
+bool DisplayHardwareBase::isScreenAcquired() const {
return mScreenAcquired;
}
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
index ef2df43..fba211b 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2012 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.
@@ -20,8 +20,6 @@
#include <stdint.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
-#include <linux/kd.h>
-#include <linux/vt.h>
#include "Barrier.h"
namespace android {
@@ -31,11 +29,13 @@ class SurfaceFlinger;
class DisplayHardwareBase
{
public:
- DisplayHardwareBase(
- const sp<SurfaceFlinger>& flinger,
- uint32_t displayIndex);
+ DisplayHardwareBase(
+ const sp<SurfaceFlinger>& flinger,
+ uint32_t displayIndex);
- ~DisplayHardwareBase();
+ ~DisplayHardwareBase();
+
+ void startSleepManagement() const;
// console management
void releaseScreen() const;
@@ -46,34 +46,21 @@ public:
private:
- class DisplayEventThreadBase : public Thread {
- protected:
+ class DisplayEventThread : public Thread {
wp<SurfaceFlinger> mFlinger;
- public:
- DisplayEventThreadBase(const sp<SurfaceFlinger>& flinger);
- virtual ~DisplayEventThreadBase();
- virtual void onFirstRef() {
- run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
- }
- virtual status_t acquireScreen() const { return NO_ERROR; };
- virtual status_t releaseScreen() const { return NO_ERROR; };
- virtual status_t initCheck() const = 0;
- };
-
- class DisplayEventThread : public DisplayEventThreadBase
- {
mutable Barrier mBarrier;
+ status_t waitForFbSleep();
+ status_t waitForFbWake();
public:
- DisplayEventThread(const sp<SurfaceFlinger>& flinger);
+ DisplayEventThread(const sp<SurfaceFlinger>& flinger);
virtual ~DisplayEventThread();
virtual bool threadLoop();
- virtual status_t readyToRun();
- virtual status_t releaseScreen() const;
- virtual status_t initCheck() const;
+ status_t releaseScreen() const;
+ status_t initCheck() const;
};
- sp<DisplayEventThreadBase> mDisplayEventThread;
- mutable int mScreenAcquired;
+ sp<DisplayEventThread> mDisplayEventThread;
+ mutable int mScreenAcquired;
};
}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp b/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp
new file mode 100644
index 0000000..187da20
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/VSyncBarrier.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+
+#include "DisplayHardware/VSyncBarrier.h"
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+VSyncBarrier::VSyncBarrier() : mFd(-EINVAL) {
+#if HAS_WAITFORVSYNC
+ mFd = open("/dev/graphics/fb0", O_RDWR);
+ if (mFd < 0) {
+ mFd = -errno;
+ }
+ // try to see if FBIO_WAITFORVSYNC is supported
+ uint32_t crt = 0;
+ int err = ioctl(mFd, FBIO_WAITFORVSYNC, &crt);
+ if (err < 0) {
+ close(mFd);
+ mFd = -EINVAL;
+ }
+#endif
+}
+
+VSyncBarrier::~VSyncBarrier() {
+ if (mFd >= 0) {
+ close(mFd);
+ }
+}
+
+status_t VSyncBarrier::initCheck() const {
+ return mFd < 0 ? mFd : status_t(NO_ERROR);
+}
+
+// this must be thread-safe
+status_t VSyncBarrier::wait(nsecs_t* timestamp) const {
+ if (mFd < 0) {
+ return mFd;
+ }
+
+ int err;
+ uint32_t crt = 0;
+ do {
+ err = ioctl(mFd, FBIO_WAITFORVSYNC, &crt);
+ } while (err<0 && errno==EINTR);
+ if (err < 0) {
+ return -errno;
+ }
+ // ideally this would come from the driver
+ timestamp[0] = systemTime();
+ return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/VSyncBarrier.h b/services/surfaceflinger/DisplayHardware/VSyncBarrier.h
new file mode 100644
index 0000000..3c32950
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/VSyncBarrier.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_
+#define ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class VSyncBarrier {
+ int mFd;
+public:
+ VSyncBarrier();
+ ~VSyncBarrier();
+ status_t initCheck() const;
+ status_t wait(nsecs_t* timestamp) const;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif /* ANDROID_SURFACE_FLINGER_VSYNCBARRIER_H_ */
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
new file mode 100644
index 0000000..af0da0b
--- /dev/null
+++ b/services/surfaceflinger/EventThread.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+
+#include "DisplayHardware/DisplayHardware.h"
+#include "DisplayEventConnection.h"
+#include "EventThread.h"
+#include "SurfaceFlinger.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+EventThread::EventThread(const sp<SurfaceFlinger>& flinger)
+ : mFlinger(flinger),
+ mHw(flinger->graphicPlane(0).displayHardware()),
+ mLastVSyncTimestamp(0),
+ mDeliveredEvents(0)
+{
+}
+
+void EventThread::onFirstRef() {
+ run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+}
+
+sp<DisplayEventConnection> EventThread::createEventConnection() const {
+ return new DisplayEventConnection(const_cast<EventThread*>(this));
+}
+
+nsecs_t EventThread::getLastVSyncTimestamp() const {
+ Mutex::Autolock _l(mLock);
+ return mLastVSyncTimestamp;
+}
+
+nsecs_t EventThread::getVSyncPeriod() const {
+ return mHw.getRefreshPeriod();
+
+}
+
+status_t EventThread::registerDisplayEventConnection(
+ const sp<DisplayEventConnection>& connection) {
+ Mutex::Autolock _l(mLock);
+ ConnectionInfo info;
+ mDisplayEventConnections.add(connection, info);
+ mCondition.signal();
+ return NO_ERROR;
+}
+
+status_t EventThread::unregisterDisplayEventConnection(
+ const wp<DisplayEventConnection>& connection) {
+ Mutex::Autolock _l(mLock);
+ mDisplayEventConnections.removeItem(connection);
+ mCondition.signal();
+ return NO_ERROR;
+}
+
+void EventThread::removeDisplayEventConnection(
+ const wp<DisplayEventConnection>& connection) {
+ Mutex::Autolock _l(mLock);
+ mDisplayEventConnections.removeItem(connection);
+}
+
+EventThread::ConnectionInfo* EventThread::getConnectionInfoLocked(
+ const wp<DisplayEventConnection>& connection) {
+ ssize_t index = mDisplayEventConnections.indexOfKey(connection);
+ if (index < 0) return NULL;
+ return &mDisplayEventConnections.editValueAt(index);
+}
+
+void EventThread::setVsyncRate(uint32_t count,
+ const wp<DisplayEventConnection>& connection) {
+ if (int32_t(count) >= 0) { // server must protect against bad params
+ Mutex::Autolock _l(mLock);
+ ConnectionInfo* info = getConnectionInfoLocked(connection);
+ if (info) {
+ const int32_t new_count = (count == 0) ? -1 : count;
+ if (info->count != new_count) {
+ info->count = new_count;
+ mCondition.signal();
+ }
+ }
+ }
+}
+
+void EventThread::requestNextVsync(
+ const wp<DisplayEventConnection>& connection) {
+ Mutex::Autolock _l(mLock);
+ ConnectionInfo* info = getConnectionInfoLocked(connection);
+ if (info && info->count < 0) {
+ info->count = 0;
+ mCondition.signal();
+ }
+}
+
+bool EventThread::threadLoop() {
+
+ nsecs_t timestamp;
+ DisplayEventReceiver::Event vsync;
+ Vector< wp<DisplayEventConnection> > displayEventConnections;
+
+ { // scope for the lock
+ Mutex::Autolock _l(mLock);
+ do {
+ // see if we need to wait for the VSYNC at all
+ do {
+ bool waitForNextVsync = false;
+ size_t count = mDisplayEventConnections.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const ConnectionInfo& info(
+ mDisplayEventConnections.valueAt(i));
+ if (info.count >= 0) {
+ // at least one continuous mode or active one-shot event
+ waitForNextVsync = true;
+ break;
+ }
+ }
+
+ if (waitForNextVsync)
+ break;
+
+ mCondition.wait(mLock);
+ } while(true);
+
+ // at least one listener requested VSYNC
+ mLock.unlock();
+ timestamp = mHw.waitForRefresh();
+ mLock.lock();
+ mDeliveredEvents++;
+ mLastVSyncTimestamp = timestamp;
+
+ // now see if we still need to report this VSYNC event
+ const size_t count = mDisplayEventConnections.size();
+ for (size_t i=0 ; i<count ; i++) {
+ bool reportVsync = false;
+ const ConnectionInfo& info(
+ mDisplayEventConnections.valueAt(i));
+ if (info.count >= 1) {
+ if (info.count==1 || (mDeliveredEvents % info.count) == 0) {
+ // continuous event, and time to report it
+ reportVsync = true;
+ }
+ } else if (info.count >= -1) {
+ ConnectionInfo& info(
+ mDisplayEventConnections.editValueAt(i));
+ if (info.count == 0) {
+ // fired this time around
+ reportVsync = true;
+ }
+ info.count--;
+ }
+ if (reportVsync) {
+ displayEventConnections.add(mDisplayEventConnections.keyAt(i));
+ }
+ }
+ } while (!displayEventConnections.size());
+
+ // dispatch vsync events to listeners...
+ vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+ vsync.header.timestamp = timestamp;
+ vsync.vsync.count = mDeliveredEvents;
+ }
+
+ const size_t count = displayEventConnections.size();
+ for (size_t i=0 ; i<count ; i++) {
+ sp<DisplayEventConnection> conn(displayEventConnections[i].promote());
+ // make sure the connection didn't die
+ if (conn != NULL) {
+ status_t err = conn->postEvent(vsync);
+ if (err == -EAGAIN || err == -EWOULDBLOCK) {
+ // The destination doesn't accept events anymore, it's probably
+ // full. For now, we just drop the events on the floor.
+ // Note that some events cannot be dropped and would have to be
+ // re-sent later. Right-now we don't have the ability to do
+ // this, but it doesn't matter for VSYNC.
+ } else if (err < 0) {
+ // handle any other error on the pipe as fatal. the only
+ // reasonable thing to do is to clean-up this connection.
+ // The most common error we'll get here is -EPIPE.
+ removeDisplayEventConnection(displayEventConnections[i]);
+ }
+ } else {
+ // somehow the connection is dead, but we still have it in our list
+ // just clean the list.
+ removeDisplayEventConnection(displayEventConnections[i]);
+ }
+ }
+
+ // clear all our references without holding mLock
+ displayEventConnections.clear();
+
+ return true;
+}
+
+status_t EventThread::readyToRun() {
+ ALOGI("EventThread ready to run.");
+ return NO_ERROR;
+}
+
+void EventThread::dump(String8& result, char* buffer, size_t SIZE) const {
+ Mutex::Autolock _l(mLock);
+ result.append("VSYNC state:\n");
+ snprintf(buffer, SIZE, " numListeners=%u, events-delivered: %u\n",
+ mDisplayEventConnections.size(), mDeliveredEvents);
+ result.append(buffer);
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
new file mode 100644
index 0000000..3a3071e
--- /dev/null
+++ b/services/surfaceflinger/EventThread.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_EVENT_THREAD_H
+#define ANDROID_SURFACE_FLINGER_EVENT_THREAD_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <gui/IDisplayEventConnection.h>
+
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <utils/KeyedVector.h>
+
+#include "DisplayEventConnection.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class SurfaceFlinger;
+class DisplayHardware;
+class DisplayEventConnection;
+
+// ---------------------------------------------------------------------------
+
+class EventThread : public Thread {
+ friend class DisplayEventConnection;
+
+public:
+ EventThread(const sp<SurfaceFlinger>& flinger);
+
+ sp<DisplayEventConnection> createEventConnection() const;
+
+ status_t registerDisplayEventConnection(
+ const sp<DisplayEventConnection>& connection);
+
+ status_t unregisterDisplayEventConnection(
+ const wp<DisplayEventConnection>& connection);
+
+ void setVsyncRate(uint32_t count,
+ const wp<DisplayEventConnection>& connection);
+
+ void requestNextVsync(const wp<DisplayEventConnection>& connection);
+
+ nsecs_t getLastVSyncTimestamp() const;
+
+ nsecs_t getVSyncPeriod() const;
+
+ void dump(String8& result, char* buffer, size_t SIZE) const;
+
+private:
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+ virtual void onFirstRef();
+
+ struct ConnectionInfo {
+ ConnectionInfo() : count(-1) { }
+
+ // count >= 1 : continuous event. count is the vsync rate
+ // count == 0 : one-shot event that has not fired
+ // count ==-1 : one-shot event that fired this round / disabled
+ // count ==-2 : one-shot event that fired the round before
+ int32_t count;
+ };
+
+ void removeDisplayEventConnection(
+ const wp<DisplayEventConnection>& connection);
+
+ ConnectionInfo* getConnectionInfoLocked(
+ const wp<DisplayEventConnection>& connection);
+
+ // constants
+ sp<SurfaceFlinger> mFlinger;
+ const DisplayHardware& mHw;
+
+ mutable Mutex mLock;
+ mutable Condition mCondition;
+
+ // protected by mLock
+ KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > mDisplayEventConnections;
+ nsecs_t mLastVSyncTimestamp;
+
+ // main thread only
+ size_t mDeliveredEvents;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif /* ANDROID_SURFACE_FLINGER_EVENT_THREAD_H */
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d4c4b1f..3e6b872 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -38,6 +38,7 @@
#include "Layer.h"
#include "SurfaceFlinger.h"
#include "SurfaceTextureLayer.h"
+#include <math.h>
#define DEBUG_RESIZE 0
@@ -54,6 +55,9 @@ Layer::Layer(SurfaceFlinger* flinger,
mCurrentTransform(0),
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mCurrentOpacity(true),
+ mRefreshPending(0),
+ mFrameLatencyNeeded(false),
+ mFrameLatencyOffset(0),
mFormat(PIXEL_FORMAT_NONE),
mGLExtensions(GLExtensions::getInstance()),
mOpaqueLayer(true),
@@ -65,6 +69,17 @@ Layer::Layer(SurfaceFlinger* flinger,
glGenTextures(1, &mTextureName);
}
+void Layer::onLayerDisplayed() {
+ if (mFrameLatencyNeeded) {
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ mFrameStats[mFrameLatencyOffset].timestamp = mSurfaceTexture->getTimestamp();
+ mFrameStats[mFrameLatencyOffset].set = systemTime();
+ mFrameStats[mFrameLatencyOffset].vsync = hw.getRefreshTimestamp();
+ mFrameLatencyOffset = (mFrameLatencyOffset + 1) % 128;
+ mFrameLatencyNeeded = false;
+ }
+}
+
void Layer::onFirstRef()
{
LayerBaseClient::onFirstRef();
@@ -83,7 +98,12 @@ void Layer::onFirstRef()
mSurfaceTexture = new SurfaceTextureLayer(mTextureName, this);
mSurfaceTexture->setFrameAvailableListener(new FrameQueuedListener(this));
mSurfaceTexture->setSynchronousMode(true);
+#ifdef USE_TRIPLE_BUFFERING
+#warning "using triple buffering"
+ mSurfaceTexture->setBufferCountServer(3);
+#else
mSurfaceTexture->setBufferCountServer(2);
+#endif
}
Layer::~Layer()
@@ -94,7 +114,7 @@ Layer::~Layer()
void Layer::onFrameQueued() {
android_atomic_inc(&mQueuedFrames);
- mFlinger->signalEvent();
+ mFlinger->signalLayerUpdate();
}
// called with SurfaceFlinger::mStateLock as soon as the layer is entered
@@ -388,16 +408,37 @@ bool Layer::isCropped() const {
// pageflip handling...
// ----------------------------------------------------------------------------
+bool Layer::onPreComposition()
+{
+ // if there was more than one pending update, request a refresh
+ if (mRefreshPending >= 2) {
+ mRefreshPending = 0;
+ return true;
+ }
+ mRefreshPending = 0;
+ return false;
+}
+
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
if (mQueuedFrames > 0) {
+
+ // if we've already called updateTexImage() without going through
+ // a composition step, we have to skip this layer at this point
+ // because we cannot call updateTeximage() without a corresponding
+ // compositionComplete() call.
+ // we'll trigger an update in onPreComposition().
+ if (mRefreshPending++) {
+ return;
+ }
+
// Capture the old state of the layer for comparisons later
const bool oldOpacity = isOpaque();
sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
// signal another event if we have more frames pending
if (android_atomic_dec(&mQueuedFrames) > 1) {
- mFlinger->signalEvent();
+ mFlinger->signalLayerUpdate();
}
if (mSurfaceTexture->updateTexImage() < NO_ERROR) {
@@ -408,6 +449,7 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions)
// update the active buffer
mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
+ mFrameLatencyNeeded = true;
const Rect crop(mSurfaceTexture->getCurrentCrop());
const uint32_t transform(mSurfaceTexture->getCurrentTransform());
@@ -499,6 +541,10 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions)
void Layer::unlockPageFlip(
const Transform& planeTransform, Region& outDirtyRegion)
{
+ if (mRefreshPending >= 2) {
+ return;
+ }
+
Region dirtyRegion(mPostedDirtyRegion);
if (!dirtyRegion.isEmpty()) {
mPostedDirtyRegion.clear();
@@ -532,9 +578,9 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
snprintf(buffer, SIZE,
" "
"format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
- " transform-hint=0x%02x, queued-frames=%d\n",
+ " transform-hint=0x%02x, queued-frames=%d, mRefreshPending=%d\n",
mFormat, w0, h0, s0,f0,
- getTransformHint(), mQueuedFrames);
+ getTransformHint(), mQueuedFrames, mRefreshPending);
result.append(buffer);
@@ -543,6 +589,32 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
}
}
+void Layer::dumpStats(String8& result, char* buffer, size_t SIZE) const
+{
+ LayerBaseClient::dumpStats(result, buffer, SIZE);
+ const size_t o = mFrameLatencyOffset;
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const nsecs_t period = hw.getRefreshPeriod();
+ result.appendFormat("%lld\n", period);
+ for (size_t i=0 ; i<128 ; i++) {
+ const size_t index = (o+i) % 128;
+ const nsecs_t time_app = mFrameStats[index].timestamp;
+ const nsecs_t time_set = mFrameStats[index].set;
+ const nsecs_t time_vsync = mFrameStats[index].vsync;
+ result.appendFormat("%lld\t%lld\t%lld\n",
+ time_app,
+ time_vsync,
+ time_set);
+ }
+ result.append("\n");
+}
+
+void Layer::clearStats()
+{
+ LayerBaseClient::clearStats();
+ memset(mFrameStats, 0, sizeof(mFrameStats));
+}
+
uint32_t Layer::getEffectiveUsage(uint32_t usage) const
{
// TODO: should we do something special if mSecure is set?
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2b9471b..bf30608 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -34,6 +34,7 @@
#include "LayerBase.h"
#include "SurfaceTextureLayer.h"
#include "Transform.h"
+#include <utils/Timers.h>
namespace android {
@@ -78,12 +79,17 @@ public:
// LayerBaseClient interface
virtual wp<IBinder> getSurfaceTextureBinder() const;
+ virtual void onLayerDisplayed();
+ virtual bool onPreComposition();
+
// only for debugging
inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; }
protected:
virtual void onFirstRef();
virtual void dump(String8& result, char* scratch, size_t size) const;
+ virtual void dumpStats(String8& result, char* buffer, size_t SIZE) const;
+ virtual void clearStats();
private:
friend class SurfaceTextureLayer;
@@ -110,6 +116,19 @@ private:
uint32_t mCurrentTransform;
uint32_t mCurrentScalingMode;
bool mCurrentOpacity;
+ size_t mRefreshPending;
+ bool mFrameLatencyNeeded;
+ int mFrameLatencyOffset;
+
+ struct Statistics {
+ Statistics() : timestamp(0), set(0), vsync(0) { }
+ nsecs_t timestamp; // buffer timestamp
+ nsecs_t set; // buffer displayed timestamp
+ nsecs_t vsync; // vsync immediately before set
+ };
+
+ // protected by mLock
+ Statistics mFrameStats[128];
// constants
PixelFormat mFormat;
@@ -121,9 +140,6 @@ private:
bool mSecure; // no screenshots
bool mProtectedByApp; // application requires protected path to external sink
Region mPostedDirtyRegion;
-
- // binder thread, transaction thread
- mutable Mutex mLock;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index f04add1..e764001 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -47,8 +47,7 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
mOrientation(0),
mPlaneOrientation(0),
mTransactionFlags(0),
- mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
- mInvalidate(0)
+ mPremultipliedAlpha(true), mName("unnamed"), mDebug(false)
{
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
mFlags = hw.getFlags();
@@ -240,7 +239,7 @@ void LayerBase::validateVisibility(const Transform& planeTransform)
for (size_t i=0 ; i<4 ; i++)
mVertices[i][1] = hw_h - mVertices[i][1];
- if (UNLIKELY(transformed)) {
+ if (CC_UNLIKELY(transformed)) {
// NOTE: here we could also punt if we have too many rectangles
// in the transparent region
if (tr.preserveRects()) {
@@ -262,23 +261,11 @@ void LayerBase::validateVisibility(const Transform& planeTransform)
mTransformedBounds = tr.makeBounds(w, h);
}
-void LayerBase::lockPageFlip(bool& recomputeVisibleRegions)
-{
+void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) {
}
void LayerBase::unlockPageFlip(
- const Transform& planeTransform, Region& outDirtyRegion)
-{
- if ((android_atomic_and(~1, &mInvalidate)&1) == 1) {
- outDirtyRegion.orSelf(visibleRegionScreen);
- }
-}
-
-void LayerBase::invalidate()
-{
- if ((android_atomic_or(1, &mInvalidate)&1) == 0) {
- mFlinger->signalEvent();
- }
+ const Transform& planeTransform, Region& outDirtyRegion) {
}
void LayerBase::drawRegion(const Region& reg) const
@@ -416,7 +403,7 @@ void LayerBase::drawWithOpenGL(const Region& clip) const
const State& s(drawingState());
GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
- if (UNLIKELY(s.alpha < 0xFF)) {
+ if (CC_UNLIKELY(s.alpha < 0xFF)) {
const GLfloat alpha = s.alpha * (1.0f/255.0f);
if (mPremultipliedAlpha) {
glColor4f(alpha, alpha, alpha, alpha);
@@ -471,13 +458,21 @@ void LayerBase::drawWithOpenGL(const Region& clip) const
void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
{
const Layer::State& s(drawingState());
+
+ snprintf(buffer, SIZE,
+ "+ %s %p (%s)\n",
+ getTypeId(), this, getName().string());
+ result.append(buffer);
+
+ s.transparentRegion.dump(result, "transparentRegion");
+ transparentRegionScreen.dump(result, "transparentRegionScreen");
+ visibleRegionScreen.dump(result, "visibleRegionScreen");
+
snprintf(buffer, SIZE,
- "+ %s %p (%s)\n"
" "
"z=%9d, pos=(%g,%g), size=(%4d,%4d), "
"isOpaque=%1d, needsDithering=%1d, invalidate=%1d, "
"alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
- getTypeId(), this, getName().string(),
s.z, s.transform.tx(), s.transform.ty(), s.w, s.h,
isOpaque(), needsDithering(), contentDirty,
s.alpha, s.flags,
@@ -486,11 +481,15 @@ void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
result.append(buffer);
}
-void LayerBase::shortDump(String8& result, char* scratch, size_t size) const
-{
+void LayerBase::shortDump(String8& result, char* scratch, size_t size) const {
LayerBase::dump(result, scratch, size);
}
+void LayerBase::dumpStats(String8& result, char* scratch, size_t SIZE) const {
+}
+
+void LayerBase::clearStats() {
+}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 7f62145..b8f7680 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -103,8 +103,6 @@ public:
Rect visibleBounds() const;
void drawRegion(const Region& reg) const;
- void invalidate();
-
virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; }
virtual sp<Layer> getLayer() const { return 0; }
@@ -204,11 +202,22 @@ public:
/** called with the state lock when the surface is removed from the
* current list */
- virtual void onRemoved() { };
-
+ virtual void onRemoved() { }
+
+ /** called after page-flip
+ */
+ virtual void onLayerDisplayed() { }
+
+ /** called before composition.
+ * returns true if the layer has pending updates.
+ */
+ virtual bool onPreComposition() { return false; }
+
/** always call base class first */
virtual void dump(String8& result, char* scratch, size_t size) const;
virtual void shortDump(String8& result, char* scratch, size_t size) const;
+ virtual void dumpStats(String8& result, char* buffer, size_t SIZE) const;
+ virtual void clearStats();
enum { // flags for doTransaction()
@@ -271,10 +280,6 @@ protected:
mutable bool mDebug;
- // atomic
- volatile int32_t mInvalidate;
-
-
public:
// called from class SurfaceFlinger
virtual ~LayerBase();
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 9441019..290fff4 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -18,178 +18,146 @@
#include <errno.h>
#include <sys/types.h>
+#include <binder/IPCThreadState.h>
+
#include <utils/threads.h>
#include <utils/Timers.h>
#include <utils/Log.h>
-#include <binder/IPCThreadState.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/BitTube.h>
#include "MessageQueue.h"
+#include "EventThread.h"
+#include "SurfaceFlinger.h"
namespace android {
// ---------------------------------------------------------------------------
-void MessageList::insert(const sp<MessageBase>& node)
-{
- LIST::iterator cur(mList.begin());
- LIST::iterator end(mList.end());
- while (cur != end) {
- if (*node < **cur) {
- mList.insert(cur, node);
- return;
- }
- ++cur;
+MessageBase::MessageBase()
+ : MessageHandler() {
+}
+
+MessageBase::~MessageBase() {
+}
+
+void MessageBase::handleMessage(const Message&) {
+ this->handler();
+ barrier.open();
+};
+
+// ---------------------------------------------------------------------------
+
+void MessageQueue::Handler::signalRefresh() {
+ if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+ mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
}
- mList.insert(++end, node);
}
-void MessageList::remove(MessageList::LIST::iterator pos)
-{
- mList.erase(pos);
+void MessageQueue::Handler::signalInvalidate() {
+ if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+ mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
+ }
+}
+
+void MessageQueue::Handler::handleMessage(const Message& message) {
+ switch (message.what) {
+ case INVALIDATE:
+ android_atomic_and(~eventMaskInvalidate, &mEventMask);
+ mQueue.mFlinger->onMessageReceived(message.what);
+ break;
+ case REFRESH:
+ android_atomic_and(~eventMaskRefresh, &mEventMask);
+ mQueue.mFlinger->onMessageReceived(message.what);
+ break;
+ }
}
// ---------------------------------------------------------------------------
MessageQueue::MessageQueue()
- : mInvalidate(false)
{
- mInvalidateMessage = new MessageBase(INVALIDATE);
}
-MessageQueue::~MessageQueue()
+MessageQueue::~MessageQueue() {
+}
+
+void MessageQueue::init(const sp<SurfaceFlinger>& flinger)
{
+ mFlinger = flinger;
+ mLooper = new Looper(true);
+ mHandler = new Handler(*this);
}
-sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout)
+void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
{
- sp<MessageBase> result;
+ mEventThread = eventThread;
+ mEvents = eventThread->createEventConnection();
+ mEventTube = mEvents->getDataChannel();
+ mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT,
+ MessageQueue::cb_eventReceiver, this);
+}
- bool again;
+void MessageQueue::waitMessage() {
do {
- const nsecs_t timeoutTime = systemTime() + timeout;
- while (true) {
- Mutex::Autolock _l(mLock);
- nsecs_t now = systemTime();
- nsecs_t nextEventTime = -1;
-
- LIST::iterator cur(mMessages.begin());
- if (cur != mMessages.end()) {
- result = *cur;
- }
-
- if (result != 0) {
- if (result->when <= now) {
- // there is a message to deliver
- mMessages.remove(cur);
- break;
- }
- nextEventTime = result->when;
- result = 0;
- }
-
- // see if we have an invalidate message
- if (mInvalidate) {
- mInvalidate = false;
- mInvalidateMessage->when = now;
- result = mInvalidateMessage;
- break;
- }
-
- if (timeout >= 0) {
- if (timeoutTime < now) {
- // we timed-out, return a NULL message
- result = 0;
- break;
- }
- if (nextEventTime > 0) {
- if (nextEventTime > timeoutTime) {
- nextEventTime = timeoutTime;
- }
- } else {
- nextEventTime = timeoutTime;
- }
- }
-
- if (nextEventTime >= 0) {
- //ALOGD("nextEventTime = %lld ms", nextEventTime);
- if (nextEventTime > 0) {
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- const nsecs_t reltime = nextEventTime - systemTime();
- if (reltime > 0) {
- mCondition.waitRelative(mLock, reltime);
- }
- }
- } else {
- //ALOGD("going to wait");
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- mCondition.wait(mLock);
- }
- }
- // here we're not holding the lock anymore
-
- if (result == 0)
- break;
-
- again = result->handler();
- if (again) {
- // the message has been processed. release our reference to it
- // without holding the lock.
- result->notify();
- result = 0;
+ IPCThreadState::self()->flushCommands();
+ int32_t ret = mLooper->pollOnce(-1);
+ switch (ret) {
+ case ALOOPER_POLL_WAKE:
+ case ALOOPER_POLL_CALLBACK:
+ continue;
+ case ALOOPER_POLL_ERROR:
+ ALOGE("ALOOPER_POLL_ERROR");
+ case ALOOPER_POLL_TIMEOUT:
+ // timeout (should not happen)
+ continue;
+ default:
+ // should not happen
+ ALOGE("Looper::pollOnce() returned unknown status %d", ret);
+ continue;
}
-
- } while (again);
-
- return result;
+ } while (true);
}
status_t MessageQueue::postMessage(
- const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
+ const sp<MessageBase>& messageHandler, nsecs_t relTime)
{
- return queueMessage(message, relTime, flags);
+ const Message dummyMessage;
+ if (relTime > 0) {
+ mLooper->sendMessageDelayed(relTime, messageHandler, dummyMessage);
+ } else {
+ mLooper->sendMessage(messageHandler, dummyMessage);
+ }
+ return NO_ERROR;
}
-status_t MessageQueue::invalidate() {
- Mutex::Autolock _l(mLock);
- mInvalidate = true;
- mCondition.signal();
- return NO_ERROR;
+void MessageQueue::invalidate() {
+// mHandler->signalInvalidate();
+ mEvents->requestNextVsync();
}
-status_t MessageQueue::queueMessage(
- const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
-{
- Mutex::Autolock _l(mLock);
- message->when = systemTime() + relTime;
- mMessages.insert(message);
-
- //ALOGD("MessageQueue::queueMessage time = %lld ms", message->when);
- //dumpLocked(message);
-
- mCondition.signal();
- return NO_ERROR;
+void MessageQueue::refresh() {
+ mEvents->requestNextVsync();
}
-void MessageQueue::dump(const sp<MessageBase>& message)
-{
- Mutex::Autolock _l(mLock);
- dumpLocked(message);
+int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
+ MessageQueue* queue = reinterpret_cast<MessageQueue *>(data);
+ return queue->eventReceiver(fd, events);
}
-void MessageQueue::dumpLocked(const sp<MessageBase>& message)
-{
- LIST::const_iterator cur(mMessages.begin());
- LIST::const_iterator end(mMessages.end());
- int c = 0;
- while (cur != end) {
- const char tick = (*cur == message) ? '>' : ' ';
- ALOGD("%c %d: msg{.what=%08x, when=%lld}",
- tick, c, (*cur)->what, (*cur)->when);
- ++cur;
- c++;
+int MessageQueue::eventReceiver(int fd, int events) {
+ ssize_t n;
+ DisplayEventReceiver::Event buffer[8];
+ while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
+ for (int i=0 ; i<n ; i++) {
+ if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ mHandler->signalRefresh();
+ break;
+ }
+ }
}
+ return 1;
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index 890f809..ea29e7e 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -23,100 +23,85 @@
#include <utils/threads.h>
#include <utils/Timers.h>
-#include <utils/List.h>
+#include <utils/Looper.h>
+
+#include <gui/DisplayEventReceiver.h>
#include "Barrier.h"
namespace android {
-// ---------------------------------------------------------------------------
-
-class MessageBase;
-
-class MessageList
-{
- List< sp<MessageBase> > mList;
- typedef List< sp<MessageBase> > LIST;
-public:
- inline LIST::iterator begin() { return mList.begin(); }
- inline LIST::const_iterator begin() const { return mList.begin(); }
- inline LIST::iterator end() { return mList.end(); }
- inline LIST::const_iterator end() const { return mList.end(); }
- inline bool isEmpty() const { return mList.empty(); }
- void insert(const sp<MessageBase>& node);
- void remove(LIST::iterator pos);
-};
+class IDisplayEventConnection;
+class EventThread;
+class SurfaceFlinger;
-// ============================================================================
+// ---------------------------------------------------------------------------
-class MessageBase :
- public LightRefBase<MessageBase>
+class MessageBase : public MessageHandler
{
public:
- nsecs_t when;
- uint32_t what;
- int32_t arg0;
-
- MessageBase() : when(0), what(0), arg0(0) { }
- MessageBase(uint32_t what, int32_t arg0=0)
- : when(0), what(what), arg0(arg0) { }
+ MessageBase();
// return true if message has a handler
- virtual bool handler() { return false; }
+ virtual bool handler() = 0;
// waits for the handler to be processed
void wait() const { barrier.wait(); }
-
- // releases all waiters. this is done automatically if
- // handler returns true
- void notify() const { barrier.open(); }
protected:
- virtual ~MessageBase() { }
+ virtual ~MessageBase();
private:
+ virtual void handleMessage(const Message& message);
+
mutable Barrier barrier;
- friend class LightRefBase<MessageBase>;
};
-inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) {
- return lhs.when < rhs.when;
-}
-
// ---------------------------------------------------------------------------
-class MessageQueue
-{
- typedef List< sp<MessageBase> > LIST;
-public:
+class MessageQueue {
+ class Handler : public MessageHandler {
+ enum {
+ eventMaskInvalidate = 0x1,
+ eventMaskRefresh = 0x2
+ };
+ MessageQueue& mQueue;
+ int32_t mEventMask;
+ public:
+ Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { }
+ virtual void handleMessage(const Message& message);
+ void signalRefresh();
+ void signalInvalidate();
+ };
- MessageQueue();
- ~MessageQueue();
+ friend class Handler;
+
+ sp<SurfaceFlinger> mFlinger;
+ sp<Looper> mLooper;
+ sp<EventThread> mEventThread;
+ sp<IDisplayEventConnection> mEvents;
+ sp<BitTube> mEventTube;
+ sp<Handler> mHandler;
- // pre-defined messages
+
+ static int cb_eventReceiver(int fd, int events, void* data);
+ int eventReceiver(int fd, int events);
+
+public:
enum {
- INVALIDATE = '_upd'
+ INVALIDATE = 0,
+ REFRESH = 1,
};
- sp<MessageBase> waitMessage(nsecs_t timeout = -1);
-
- status_t postMessage(const sp<MessageBase>& message,
- nsecs_t reltime=0, uint32_t flags = 0);
-
- status_t invalidate();
-
- void dump(const sp<MessageBase>& message);
+ MessageQueue();
+ ~MessageQueue();
+ void init(const sp<SurfaceFlinger>& flinger);
+ void setEventThread(const sp<EventThread>& events);
-private:
- status_t queueMessage(const sp<MessageBase>& message,
- nsecs_t reltime, uint32_t flags);
- void dumpLocked(const sp<MessageBase>& message);
-
- Mutex mLock;
- Condition mCondition;
- MessageList mMessages;
- bool mInvalidate;
- sp<MessageBase> mInvalidateMessage;
+ void waitMessage();
+ status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0);
+ void invalidate();
+ void refresh();
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 98277b4..ab09bfa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -34,18 +34,21 @@
#include <binder/MemoryHeapBase.h>
#include <binder/PermissionCache.h>
+#include <gui/IDisplayEventConnection.h>
+
#include <utils/String8.h>
#include <utils/String16.h>
#include <utils/StopWatch.h>
#include <ui/GraphicBufferAllocator.h>
-#include <ui/GraphicLog.h>
#include <ui/PixelFormat.h>
#include <pixelflinger/pixelflinger.h>
#include <GLES/gl.h>
#include "clz.h"
+#include "DisplayEventConnection.h"
+#include "EventThread.h"
#include "GLExtensions.h"
#include "DdmConnection.h"
#include "Layer.h"
@@ -56,15 +59,9 @@
#include "DisplayHardware/DisplayHardware.h"
#include "DisplayHardware/HWComposer.h"
+#include <private/android_filesystem_config.h>
#include <private/surfaceflinger/SharedBufferStack.h>
-/* ideally AID_GRAPHICS would be in a semi-public header
- * or there would be a way to map a user/group name to its id
- */
-#ifndef AID_GRAPHICS
-#define AID_GRAPHICS 1003
-#endif
-
#define EGL_VERSION_HW_ANDROID 0x3143
#define DISPLAY_COUNT 1
@@ -128,11 +125,34 @@ void SurfaceFlinger::init()
ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
}
+void SurfaceFlinger::onFirstRef()
+{
+ mEventQueue.init(this);
+
+ run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
+
+ // Wait for the main thread to be done with its initialization
+ mReadyToRunBarrier.wait();
+}
+
+
SurfaceFlinger::~SurfaceFlinger()
{
glDeleteTextures(1, &mWormholeTexName);
}
+void SurfaceFlinger::binderDied(const wp<IBinder>& who)
+{
+ // the window manager died on us. prepare its eulogy.
+
+ // reset screen orientation
+ Vector<ComposerState> state;
+ setTransactionState(state, eOrientationDefault, 0);
+
+ // restart the boot-animation
+ property_set("ctl.start", "bootanim");
+}
+
sp<IMemoryHeap> SurfaceFlinger::getCblk() const
{
return mServerHeap;
@@ -186,25 +206,6 @@ void SurfaceFlinger::bootFinished()
property_set("ctl.stop", "bootanim");
}
-void SurfaceFlinger::binderDied(const wp<IBinder>& who)
-{
- // the window manager died on us. prepare its eulogy.
-
- // reset screen orientation
- setOrientation(0, eOrientationDefault, 0);
-
- // restart the boot-animation
- property_set("ctl.start", "bootanim");
-}
-
-void SurfaceFlinger::onFirstRef()
-{
- run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
-
- // Wait for the main thread to be done with its initialization
- mReadyToRunBarrier.wait();
-}
-
static inline uint16_t pack565(int r, int g, int b) {
return (r<<11)|(g<<5)|b;
}
@@ -295,12 +296,18 @@ status_t SurfaceFlinger::readyToRun()
// put the origin in the left-bottom corner
glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h
- mReadyToRunBarrier.open();
+
+ // start the EventThread
+ mEventThread = new EventThread(this);
+ mEventQueue.setEventThread(mEventThread);
+ hw.startSleepManagement();
/*
* We're now ready to accept clients...
*/
+ mReadyToRunBarrier.open();
+
// start boot animation
property_set("ctl.start", "bootanim");
@@ -308,29 +315,6 @@ status_t SurfaceFlinger::readyToRun()
}
// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Events Handler
-#endif
-
-void SurfaceFlinger::waitForEvent()
-{
- while (true) {
- nsecs_t timeout = -1;
- sp<MessageBase> msg = mEventQueue.waitMessage(timeout);
- if (msg != 0) {
- switch (msg->what) {
- case MessageQueue::INVALIDATE:
- // invalidate message, just return to the main loop
- return;
- }
- }
- }
-}
-
-void SurfaceFlinger::signalEvent() {
- mEventQueue.invalidate();
-}
bool SurfaceFlinger::authenticateSurfaceTexture(
const sp<ISurfaceTexture>& surfaceTexture) const {
@@ -373,92 +357,121 @@ bool SurfaceFlinger::authenticateSurfaceTexture(
return false;
}
+// ----------------------------------------------------------------------------
+
+sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
+ return mEventThread->createEventConnection();
+}
+
+// ----------------------------------------------------------------------------
+
+void SurfaceFlinger::waitForEvent() {
+ mEventQueue.waitMessage();
+}
+
+void SurfaceFlinger::signalTransaction() {
+ mEventQueue.invalidate();
+}
+
+void SurfaceFlinger::signalLayerUpdate() {
+ mEventQueue.invalidate();
+}
+
+void SurfaceFlinger::signalRefresh() {
+ mEventQueue.refresh();
+}
+
status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
- nsecs_t reltime, uint32_t flags)
-{
- return mEventQueue.postMessage(msg, reltime, flags);
+ nsecs_t reltime, uint32_t flags) {
+ return mEventQueue.postMessage(msg, reltime);
}
status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
- nsecs_t reltime, uint32_t flags)
-{
- status_t res = mEventQueue.postMessage(msg, reltime, flags);
+ nsecs_t reltime, uint32_t flags) {
+ status_t res = mEventQueue.postMessage(msg, reltime);
if (res == NO_ERROR) {
msg->wait();
}
return res;
}
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Main loop
-#endif
-
bool SurfaceFlinger::threadLoop()
{
waitForEvent();
+ return true;
+}
- // check for transactions
- if (UNLIKELY(mConsoleSignals)) {
- handleConsoleEvents();
- }
-
- // if we're in a global transaction, don't do anything.
- const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
- uint32_t transactionFlags = peekTransactionFlags(mask);
- if (UNLIKELY(transactionFlags)) {
- handleTransaction(transactionFlags);
- }
+void SurfaceFlinger::onMessageReceived(int32_t what)
+{
+ switch (what) {
+ case MessageQueue::REFRESH: {
+// case MessageQueue::INVALIDATE: {
+ // check for transactions
+ if (CC_UNLIKELY(mConsoleSignals)) {
+ handleConsoleEvents();
+ }
- // post surfaces (if needed)
- handlePageFlip();
+ // if we're in a global transaction, don't do anything.
+ const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
+ uint32_t transactionFlags = peekTransactionFlags(mask);
+ if (CC_UNLIKELY(transactionFlags)) {
+ handleTransaction(transactionFlags);
+ }
- if (mDirtyRegion.isEmpty()) {
- // nothing new to do.
- return true;
- }
+ // post surfaces (if needed)
+ handlePageFlip();
- if (UNLIKELY(mHwWorkListDirty)) {
- // build the h/w work list
- handleWorkList();
- }
+// signalRefresh();
+//
+// } break;
+//
+// case MessageQueue::REFRESH: {
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- if (LIKELY(hw.canDraw())) {
- // repaint the framebuffer (if needed)
+ handleRefresh();
- const int index = hw.getCurrentBufferIndex();
- GraphicLog& logger(GraphicLog::getInstance());
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
- logger.log(GraphicLog::SF_REPAINT, index);
- handleRepaint();
+// if (mDirtyRegion.isEmpty()) {
+// return;
+// }
- // inform the h/w that we're done compositing
- logger.log(GraphicLog::SF_COMPOSITION_COMPLETE, index);
- hw.compositionComplete();
+ if (CC_UNLIKELY(mHwWorkListDirty)) {
+ // build the h/w work list
+ handleWorkList();
+ }
- logger.log(GraphicLog::SF_SWAP_BUFFERS, index);
- postFramebuffer();
+ if (CC_LIKELY(hw.canDraw())) {
+ // repaint the framebuffer (if needed)
+ handleRepaint();
+ // inform the h/w that we're done compositing
+ hw.compositionComplete();
+ postFramebuffer();
+ } else {
+ // pretend we did the post
+ hw.compositionComplete();
+ }
- logger.log(GraphicLog::SF_REPAINT_DONE, index);
- } else {
- // pretend we did the post
- hw.compositionComplete();
- usleep(16667); // 60 fps period
+ } break;
}
- return true;
}
void SurfaceFlinger::postFramebuffer()
{
- // this should never happen. we do the flip anyways so we don't
- // risk to cause a deadlock with hwc
- ALOGW_IF(mSwapRegion.isEmpty(), "mSwapRegion is empty");
+ // mSwapRegion can be empty here is some cases, for instance if a hidden
+ // or fully transparent window is updating.
+ // in that case, we need to flip anyways to not risk a deadlock with
+ // h/w composer.
+
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const nsecs_t now = systemTime();
mDebugInSwapBuffers = now;
hw.flip(mSwapRegion);
+
+ size_t numLayers = mVisibleLayersSortedByZ.size();
+ for (size_t i = 0; i < numLayers; i++) {
+ mVisibleLayersSortedByZ[i]->onLayerDisplayed();
+ }
+
mLastSwapBufferTime = systemTime() - now;
mDebugInSwapBuffers = 0;
mSwapRegion.clear();
@@ -625,7 +638,7 @@ void SurfaceFlinger::computeVisibleRegions(
// handle hidden surfaces by setting the visible region to empty
- if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
+ if (CC_LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
const bool translucent = !layer->isOpaque();
const Rect bounds(layer->visibleBounds());
visibleRegion.set(bounds);
@@ -725,13 +738,13 @@ void SurfaceFlinger::commitTransaction()
void SurfaceFlinger::handlePageFlip()
{
- bool visibleRegions = mVisibleRegionsDirty;
+ const DisplayHardware& hw = graphicPlane(0).displayHardware();
+ const Region screenRegion(hw.bounds());
+
const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
- visibleRegions |= lockPageFlip(currentLayers);
+ const bool visibleRegions = lockPageFlip(currentLayers);
- const DisplayHardware& hw = graphicPlane(0).displayHardware();
- const Region screenRegion(hw.bounds());
- if (visibleRegions) {
+ if (visibleRegions || mVisibleRegionsDirty) {
Region opaqueRegion;
computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
@@ -778,7 +791,7 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
{
const GraphicPlane& plane(graphicPlane(0));
const Transform& planeTransform(plane.transform());
- size_t count = currentLayers.size();
+ const size_t count = currentLayers.size();
sp<LayerBase> const* layers = currentLayers.array();
for (size_t i=0 ; i<count ; i++) {
const sp<LayerBase>& layer(layers[i]);
@@ -786,6 +799,23 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
}
}
+void SurfaceFlinger::handleRefresh()
+{
+ bool needInvalidate = false;
+ const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(currentLayers[i]);
+ if (layer->onPreComposition()) {
+ needInvalidate = true;
+ }
+ }
+ if (needInvalidate) {
+ signalLayerUpdate();
+ }
+}
+
+
void SurfaceFlinger::handleWorkList()
{
mHwWorkListDirty = false;
@@ -810,7 +840,7 @@ void SurfaceFlinger::handleRepaint()
// compute the invalid region
mSwapRegion.orSelf(mDirtyRegion);
- if (UNLIKELY(mDebugRegion)) {
+ if (CC_UNLIKELY(mDebugRegion)) {
debugFlashRegions();
}
@@ -964,7 +994,7 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty)
HWComposer& hwc(hw.getHwComposer());
const size_t fbLayerCount = hwc.getLayerCount(HWC_FRAMEBUFFER);
- if (UNLIKELY(fbLayerCount && !mWormholeRegion.isEmpty())) {
+ if (CC_UNLIKELY(fbLayerCount && !mWormholeRegion.isEmpty())) {
// should never happen unless the window manager has a bug
// draw something...
drawWormhole();
@@ -1049,7 +1079,7 @@ void SurfaceFlinger::drawWormhole() const
const int32_t width = hw.getWidth();
const int32_t height = hw.getHeight();
- if (LIKELY(!mDebugBackground)) {
+ if (CC_LIKELY(!mDebugBackground)) {
glClearColor(0,0,0,0);
Region::const_iterator it = region.begin();
Region::const_iterator const end = region.end();
@@ -1093,23 +1123,6 @@ void SurfaceFlinger::drawWormhole() const
}
}
-void SurfaceFlinger::debugShowFPS() const
-{
- static int mFrameCount;
- static int mLastFrameCount = 0;
- static nsecs_t mLastFpsTime = 0;
- static float mFps = 0;
- mFrameCount++;
- nsecs_t now = systemTime();
- nsecs_t diff = now - mLastFpsTime;
- if (diff > ms2ns(250)) {
- mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff;
- mLastFpsTime = now;
- mLastFrameCount = mFrameCount;
- }
- // XXX: mFPS has the value we want
- }
-
status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer)
{
Mutex::Autolock _l(mStateLock);
@@ -1200,7 +1213,7 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags)
{
uint32_t old = android_atomic_or(flags, &mTransactionFlags);
if ((old & flags)==0) { // wake the server up
- signalEvent();
+ signalTransaction();
}
return old;
}
@@ -1250,26 +1263,6 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state,
}
}
-int SurfaceFlinger::setOrientation(DisplayID dpy,
- int orientation, uint32_t flags)
-{
- if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
- return BAD_VALUE;
-
- Mutex::Autolock _l(mStateLock);
- if (mCurrentState.orientation != orientation) {
- if (uint32_t(orientation)<=eOrientation270 || orientation==42) {
- mCurrentState.orientationFlags = flags;
- mCurrentState.orientation = orientation;
- setTransactionFlags(eTransactionNeeded);
- mTransactionCV.wait(mStateLock);
- } else {
- orientation = BAD_VALUE;
- }
- }
- return orientation;
-}
-
sp<ISurface> SurfaceFlinger::createSurface(
ISurfaceComposerClient::surface_data_t* params,
const String8& name,
@@ -1352,7 +1345,7 @@ sp<Layer> SurfaceFlinger::createNormalSurface(
sp<Layer> layer = new Layer(this, display, client);
status_t err = layer->setBuffers(w, h, format, flags);
- if (LIKELY(err != NO_ERROR)) {
+ if (CC_LIKELY(err != NO_ERROR)) {
ALOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));
layer.clear();
}
@@ -1471,14 +1464,14 @@ void SurfaceFlinger::screenReleased(int dpy)
{
// this may be called by a signal handler, we can't do too much in here
android_atomic_or(eConsoleReleased, &mConsoleSignals);
- signalEvent();
+ signalTransaction();
}
void SurfaceFlinger::screenAcquired(int dpy)
{
// this may be called by a signal handler, we can't do too much in here
android_atomic_or(eConsoleAcquired, &mConsoleSignals);
- signalEvent();
+ signalTransaction();
}
status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
@@ -1494,14 +1487,6 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
IPCThreadState::self()->getCallingUid());
result.append(buffer);
} else {
-
- // figure out if we're stuck somewhere
- const nsecs_t now = systemTime();
- const nsecs_t inSwapBuffers(mDebugInSwapBuffers);
- const nsecs_t inTransaction(mDebugInTransaction);
- nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0;
- nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
-
// Try to get the main lock, but don't insist if we can't
// (this would indicate SF is stuck, but we want to be able to
// print something in dumpsys).
@@ -1517,110 +1502,205 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
result.append(buffer);
}
- /*
- * Dump the visible layer list
- */
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- const size_t count = currentLayers.size();
- snprintf(buffer, SIZE, "Visible layers (count = %d)\n", count);
- result.append(buffer);
- for (size_t i=0 ; i<count ; i++) {
- const sp<LayerBase>& layer(currentLayers[i]);
- layer->dump(result, buffer, SIZE);
- const Layer::State& s(layer->drawingState());
- s.transparentRegion.dump(result, "transparentRegion");
- layer->transparentRegionScreen.dump(result, "transparentRegionScreen");
- layer->visibleRegionScreen.dump(result, "visibleRegionScreen");
- }
-
- /*
- * Dump the layers in the purgatory
- */
+ bool dumpAll = true;
+ size_t index = 0;
+ size_t numArgs = args.size();
+ if (numArgs) {
+ dumpAll = false;
- 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++) {
- const sp<LayerBase>& layer(mLayerPurgatory.itemAt(i));
- layer->shortDump(result, buffer, SIZE);
- }
+ if ((index < numArgs) &&
+ (args[index] == String16("--list"))) {
+ index++;
+ listLayersLocked(args, index, result, buffer, SIZE);
+ }
- /*
- * Dump SurfaceFlinger global state
- */
+ if ((index < numArgs) &&
+ (args[index] == String16("--latency"))) {
+ index++;
+ dumpStatsLocked(args, index, result, buffer, SIZE);
+ }
- snprintf(buffer, SIZE, "SurfaceFlinger global state:\n");
- result.append(buffer);
+ if ((index < numArgs) &&
+ (args[index] == String16("--latency-clear"))) {
+ index++;
+ clearStatsLocked(args, index, result, buffer, SIZE);
+ }
+ }
- const GLExtensions& extensions(GLExtensions::getInstance());
- snprintf(buffer, SIZE, "GLES: %s, %s, %s\n",
- extensions.getVendor(),
- extensions.getRenderer(),
- extensions.getVersion());
- result.append(buffer);
+ if (dumpAll) {
+ dumpAllLocked(result, buffer, SIZE);
+ }
- snprintf(buffer, SIZE, "EGL : %s\n",
- eglQueryString(graphicPlane(0).getEGLDisplay(),
- EGL_VERSION_HW_ANDROID));
- result.append(buffer);
+ if (locked) {
+ mStateLock.unlock();
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
- snprintf(buffer, SIZE, "EXTS: %s\n", extensions.getExtension());
+void SurfaceFlinger::listLayersLocked(const Vector<String16>& args, size_t& index,
+ String8& result, char* buffer, size_t SIZE) const
+{
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(currentLayers[i]);
+ snprintf(buffer, SIZE, "%s\n", layer->getName().string());
result.append(buffer);
+ }
+}
- mWormholeRegion.dump(result, "WormholeRegion");
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- snprintf(buffer, SIZE,
- " orientation=%d, canDraw=%d\n",
- mCurrentState.orientation, hw.canDraw());
- result.append(buffer);
- snprintf(buffer, SIZE,
- " last eglSwapBuffers() time: %f us\n"
- " last transaction time : %f us\n"
- " refresh-rate : %f fps\n"
- " x-dpi : %f\n"
- " y-dpi : %f\n",
- mLastSwapBufferTime/1000.0,
- mLastTransactionTime/1000.0,
- hw.getRefreshRate(),
- hw.getDpiX(),
- hw.getDpiY());
- result.append(buffer);
+void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
+ String8& result, char* buffer, size_t SIZE) const
+{
+ String8 name;
+ if (index < args.size()) {
+ name = String8(args[index]);
+ index++;
+ }
- if (inSwapBuffersDuration || !locked) {
- snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n",
- inSwapBuffersDuration/1000.0);
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(currentLayers[i]);
+ if (name.isEmpty()) {
+ snprintf(buffer, SIZE, "%s\n", layer->getName().string());
result.append(buffer);
}
+ if (name.isEmpty() || (name == layer->getName())) {
+ layer->dumpStats(result, buffer, SIZE);
+ }
+ }
+}
- if (inTransactionDuration || !locked) {
- snprintf(buffer, SIZE, " transaction time: %f us\n",
- inTransactionDuration/1000.0);
- result.append(buffer);
+void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index,
+ String8& result, char* buffer, size_t SIZE) const
+{
+ String8 name;
+ if (index < args.size()) {
+ name = String8(args[index]);
+ index++;
+ }
+
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(currentLayers[i]);
+ if (name.isEmpty() || (name == layer->getName())) {
+ layer->clearStats();
}
+ }
+}
- /*
- * Dump HWComposer state
- */
- HWComposer& hwc(hw.getHwComposer());
- snprintf(buffer, SIZE, " h/w composer %s and %s\n",
- hwc.initCheck()==NO_ERROR ? "present" : "not present",
- (mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled");
- result.append(buffer);
- hwc.dump(result, buffer, SIZE, mVisibleLayersSortedByZ);
+void SurfaceFlinger::dumpAllLocked(
+ String8& result, char* buffer, size_t SIZE) const
+{
+ // figure out if we're stuck somewhere
+ const nsecs_t now = systemTime();
+ const nsecs_t inSwapBuffers(mDebugInSwapBuffers);
+ const nsecs_t inTransaction(mDebugInTransaction);
+ nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0;
+ nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
- /*
- * Dump gralloc state
- */
- const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
- alloc.dump(result);
- hw.dump(result);
+ /*
+ * Dump the visible layer list
+ */
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ snprintf(buffer, SIZE, "Visible layers (count = %d)\n", count);
+ result.append(buffer);
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(currentLayers[i]);
+ layer->dump(result, buffer, SIZE);
+ }
- if (locked) {
- mStateLock.unlock();
- }
+ /*
+ * Dump the layers in the purgatory
+ */
+
+ 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++) {
+ const sp<LayerBase>& layer(mLayerPurgatory.itemAt(i));
+ layer->shortDump(result, buffer, SIZE);
}
- write(fd, result.string(), result.size());
- return NO_ERROR;
+
+ /*
+ * Dump SurfaceFlinger global state
+ */
+
+ snprintf(buffer, SIZE, "SurfaceFlinger global state:\n");
+ result.append(buffer);
+
+ const GLExtensions& extensions(GLExtensions::getInstance());
+ snprintf(buffer, SIZE, "GLES: %s, %s, %s\n",
+ extensions.getVendor(),
+ 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,
+ " orientation=%d, canDraw=%d\n",
+ mCurrentState.orientation, hw.canDraw());
+ result.append(buffer);
+ snprintf(buffer, SIZE,
+ " last eglSwapBuffers() time: %f us\n"
+ " last transaction time : %f us\n"
+ " transaction-flags : %08x\n"
+ " refresh-rate : %f fps\n"
+ " x-dpi : %f\n"
+ " y-dpi : %f\n",
+ mLastSwapBufferTime/1000.0,
+ mLastTransactionTime/1000.0,
+ mTransactionFlags,
+ hw.getRefreshRate(),
+ hw.getDpiX(),
+ hw.getDpiY());
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n",
+ inSwapBuffersDuration/1000.0);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, " transaction time: %f us\n",
+ inTransactionDuration/1000.0);
+ result.append(buffer);
+
+ /*
+ * VSYNC state
+ */
+ mEventThread->dump(result, buffer, SIZE);
+
+ /*
+ * Dump HWComposer state
+ */
+ HWComposer& hwc(hw.getHwComposer());
+ snprintf(buffer, SIZE, "h/w composer state:\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, " h/w composer %s and %s\n",
+ hwc.initCheck()==NO_ERROR ? "present" : "not present",
+ (mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled");
+ result.append(buffer);
+ hwc.dump(result, buffer, SIZE, mVisibleLayersSortedByZ);
+
+ /*
+ * Dump gralloc state
+ */
+ const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
+ alloc.dump(result);
+ hw.dump(result);
}
status_t SurfaceFlinger::onTransact(
@@ -1665,7 +1745,7 @@ status_t SurfaceFlinger::onTransact(
status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- if (UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) {
+ if (CC_UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
@@ -1696,11 +1776,6 @@ status_t SurfaceFlinger::onTransact(
setTransactionFlags(eTransactionNeeded|eTraversalNeeded);
return NO_ERROR;
}
- case 1006:{ // enable/disable GraphicLog
- int enabled = data.readInt32();
- GraphicLog::getInstance().setEnabled(enabled);
- return NO_ERROR;
- }
case 1008: // toggle use of hw composer
n = data.readInt32();
mDebugDisableHWC = n ? 1 : 0;
@@ -1734,7 +1809,7 @@ void SurfaceFlinger::repaintEverything() {
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const Rect bounds(hw.getBounds());
setInvalidateRegion(Region(bounds));
- signalEvent();
+ signalTransaction();
}
void SurfaceFlinger::setInvalidateRegion(const Region& reg) {
@@ -2210,7 +2285,7 @@ status_t SurfaceFlinger::turnElectronBeamOnImplLocked(int32_t mode)
// make sure to redraw the whole screen when the animation is done
mDirtyRegion.set(hw.bounds());
- signalEvent();
+ signalTransaction();
return NO_ERROR;
}
@@ -2250,7 +2325,7 @@ status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
status_t result = PERMISSION_DENIED;
// only one display supported for now
- if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+ if (CC_UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
return BAD_VALUE;
if (!GLExtensions::getInstance().haveFramebufferObject())
@@ -2372,7 +2447,7 @@ status_t SurfaceFlinger::captureScreen(DisplayID dpy,
uint32_t minLayerZ, uint32_t maxLayerZ)
{
// only one display supported for now
- if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+ if (CC_UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
return BAD_VALUE;
if (!GLExtensions::getInstance().haveFramebufferObject())
@@ -2500,7 +2575,7 @@ status_t Client::onTransact(
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
const int self_pid = getpid();
- if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) {
+ if (CC_UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) {
// we're called from a different process, do the real check
if (!PermissionCache::checkCallingPermission(sAccessSurfaceFlinger))
{
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 17b80a6..fcd9361 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -20,6 +20,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <cutils/compiler.h>
+
#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
@@ -46,14 +48,13 @@ namespace android {
class Client;
class DisplayHardware;
+class DisplayEventConnection;
+class EventThread;
class Layer;
class LayerDim;
class LayerScreenshot;
struct surface_flinger_cblk_t;
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
-
// ---------------------------------------------------------------------------
class Client : public BnSurfaceComposerClient
@@ -169,8 +170,8 @@ public:
virtual void bootFinished();
virtual void setTransactionState(const Vector<ComposerState>& state,
int orientation, uint32_t flags);
- virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
virtual bool authenticateSurfaceTexture(const sp<ISurfaceTexture>& surface) const;
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection();
virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
@@ -189,6 +190,8 @@ public:
status_t renderScreenToTextureLocked(DisplayID dpy,
GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
+ void onMessageReceived(int32_t what);
+
status_t postMessageAsync(const sp<MessageBase>& msg,
nsecs_t reltime=0, uint32_t flags = 0);
@@ -222,6 +225,7 @@ private:
private:
friend class Client;
+ friend class DisplayEventConnection;
friend class LayerBase;
friend class LayerBaseClient;
friend class Layer;
@@ -281,7 +285,10 @@ private:
public: // hack to work around gcc 4.0.3 bug
const GraphicPlane& graphicPlane(int dpy) const;
GraphicPlane& graphicPlane(int dpy);
- void signalEvent();
+
+ void signalTransaction();
+ void signalLayerUpdate();
+ void signalRefresh();
void repaintEverything();
private:
@@ -298,6 +305,7 @@ private:
void handlePageFlip();
bool lockPageFlip(const LayerVector& currentLayers);
void unlockPageFlip(const LayerVector& currentLayers);
+ void handleRefresh();
void handleWorkList();
void handleRepaint();
void postFramebuffer();
@@ -332,9 +340,15 @@ private:
status_t electronBeamOnAnimationImplLocked();
void debugFlashRegions();
- void debugShowFPS() const;
void drawWormhole() const;
+ void listLayersLocked(const Vector<String16>& args, size_t& index,
+ String8& result, char* buffer, size_t SIZE) const;
+ void dumpStatsLocked(const Vector<String16>& args, size_t& index,
+ String8& result, char* buffer, size_t SIZE) const;
+ void clearStatsLocked(const Vector<String16>& args, size_t& index,
+ String8& result, char* buffer, size_t SIZE) const;
+ void dumpAllLocked(String8& result, char* buffer, size_t SIZE) const;
mutable MessageQueue mEventQueue;
@@ -362,6 +376,7 @@ private:
GLuint mWormholeTexName;
GLuint mProtectedTexName;
nsecs_t mBootTime;
+ sp<EventThread> mEventThread;
// Can only accessed from the main thread, these members
// don't need synchronization
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index 259b937..49e8e63 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -94,6 +94,10 @@ status_t SurfaceTextureLayer::connect(int api,
*outTransform = orientation;
}
switch(api) {
+ case NATIVE_WINDOW_API_CPU:
+ // SurfaceTextureClient supports only 2 buffers for CPU connections
+ this->setBufferCountServer(2);
+ break;
case NATIVE_WINDOW_API_MEDIA:
case NATIVE_WINDOW_API_CAMERA:
// Camera preview and videos are rate-limited on the producer
diff --git a/services/surfaceflinger/tests/vsync/Android.mk b/services/surfaceflinger/tests/vsync/Android.mk
new file mode 100644
index 0000000..9181760
--- /dev/null
+++ b/services/surfaceflinger/tests/vsync/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ vsync.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libui \
+ libgui
+
+LOCAL_MODULE:= test-vsync-events
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp
new file mode 100644
index 0000000..b0d54c4
--- /dev/null
+++ b/services/surfaceflinger/tests/vsync/vsync.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Looper.h>
+
+using namespace android;
+
+int receiver(int fd, int events, void* data)
+{
+ DisplayEventReceiver* q = (DisplayEventReceiver*)data;
+
+ ssize_t n;
+ DisplayEventReceiver::Event buffer[1];
+
+ static nsecs_t oldTimeStamp = 0;
+
+ while ((n = q->getEvents(buffer, 1)) > 0) {
+ for (int i=0 ; i<n ; i++) {
+ if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ printf("event vsync: count=%d\t", buffer[i].vsync.count);
+ }
+ if (oldTimeStamp) {
+ float t = float(buffer[i].header.timestamp - oldTimeStamp) / s2ns(1);
+ printf("%f ms (%f Hz)\n", t*1000, 1.0/t);
+ }
+ oldTimeStamp = buffer[i].header.timestamp;
+ }
+ }
+ if (n<0) {
+ printf("error reading events (%s)\n", strerror(-n));
+ }
+ return 1;
+}
+
+int main(int argc, char** argv)
+{
+ DisplayEventReceiver myDisplayEvent;
+
+
+ sp<Looper> loop = new Looper(false);
+ loop->addFd(myDisplayEvent.getFd(), 0, ALOOPER_EVENT_INPUT, receiver,
+ &myDisplayEvent);
+
+ myDisplayEvent.setVsyncRate(1);
+
+ do {
+ //printf("about to poll...\n");
+ int32_t ret = loop->pollOnce(-1);
+ switch (ret) {
+ case ALOOPER_POLL_WAKE:
+ //("ALOOPER_POLL_WAKE\n");
+ break;
+ case ALOOPER_POLL_CALLBACK:
+ //("ALOOPER_POLL_CALLBACK\n");
+ break;
+ case ALOOPER_POLL_TIMEOUT:
+ printf("ALOOPER_POLL_TIMEOUT\n");
+ break;
+ case ALOOPER_POLL_ERROR:
+ printf("ALOOPER_POLL_TIMEOUT\n");
+ break;
+ default:
+ printf("ugh? poll returned %d\n", ret);
+ break;
+ }
+ } while (1);
+
+ return 0;
+}
diff --git a/services/surfaceflinger/tests/waitforvsync/Android.mk b/services/surfaceflinger/tests/waitforvsync/Android.mk
new file mode 100644
index 0000000..c25f5ab
--- /dev/null
+++ b/services/surfaceflinger/tests/waitforvsync/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ waitforvsync.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+
+LOCAL_MODULE:= test-waitforvsync
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp b/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp
new file mode 100644
index 0000000..279b88b
--- /dev/null
+++ b/services/surfaceflinger/tests/waitforvsync/waitforvsync.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+
+int main(int argc, char** argv) {
+ int fd = open("/dev/graphics/fb0", O_RDWR);
+ if (fd >= 0) {
+ do {
+ uint32_t crt = 0;
+ int err = ioctl(fd, FBIO_WAITFORVSYNC, &crt);
+ if (err < 0) {
+ printf("FBIO_WAITFORVSYNC error: %s\n", strerror(errno));
+ break;
+ }
+ } while(1);
+ close(fd);
+ }
+ return 0;
+}
diff --git a/services/tests/servicestests/res/raw/netstats_uid_v4 b/services/tests/servicestests/res/raw/netstats_uid_v4
new file mode 100644
index 0000000..e75fc1c
--- /dev/null
+++ b/services/tests/servicestests/res/raw/netstats_uid_v4
Binary files differ
diff --git a/services/tests/servicestests/res/raw/netstats_v1 b/services/tests/servicestests/res/raw/netstats_v1
new file mode 100644
index 0000000..e80860a
--- /dev/null
+++ b/services/tests/servicestests/res/raw/netstats_v1
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 636ba21..21a8ec2 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -23,9 +23,9 @@ import android.test.AndroidTestCase;
import java.io.File;
/**
- * Tests for {@link com.android.server.EntropyService}
+ * Tests for {@link com.android.server.EntropyMixer}
*/
-public class EntropyServiceTest extends AndroidTestCase {
+public class EntropyMixerTest extends AndroidTestCase {
public void testInitialWrite() throws Exception {
File dir = getContext().getDir("testInitialWrite", Context.MODE_PRIVATE);
@@ -34,7 +34,7 @@ public class EntropyServiceTest extends AndroidTestCase {
assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
// The constructor has the side effect of writing to file
- new EntropyService("/dev/null", file.getCanonicalPath());
+ new EntropyMixer("/dev/null", file.getCanonicalPath());
assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
}
diff --git a/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java b/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java
new file mode 100644
index 0000000..275d807
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.server.NativeDaemonConnector.appendEscaped;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+/**
+ * Tests for {@link NativeDaemonConnector}.
+ */
+@MediumTest
+public class NativeDaemonConnectorTest extends AndroidTestCase {
+ private static final String TAG = "NativeDaemonConnectorTest";
+
+ public void testArgumentNormal() throws Exception {
+ final StringBuilder builder = new StringBuilder();
+
+ builder.setLength(0);
+ appendEscaped(builder, "");
+ assertEquals("", builder.toString());
+
+ builder.setLength(0);
+ appendEscaped(builder, "foo");
+ assertEquals("foo", builder.toString());
+
+ builder.setLength(0);
+ appendEscaped(builder, "foo\"bar");
+ assertEquals("foo\\\"bar", builder.toString());
+
+ builder.setLength(0);
+ appendEscaped(builder, "foo\\bar\\\"baz");
+ assertEquals("foo\\\\bar\\\\\\\"baz", builder.toString());
+ }
+
+ public void testArgumentWithSpaces() throws Exception {
+ final StringBuilder builder = new StringBuilder();
+
+ builder.setLength(0);
+ appendEscaped(builder, "foo bar");
+ assertEquals("\"foo bar\"", builder.toString());
+
+ builder.setLength(0);
+ appendEscaped(builder, "foo\"bar\\baz foo");
+ assertEquals("\"foo\\\"bar\\\\baz foo\"", builder.toString());
+ }
+
+ public void testArgumentWithUtf() throws Exception {
+ final StringBuilder builder = new StringBuilder();
+
+ builder.setLength(0);
+ appendEscaped(builder, "caf\u00E9 c\u00F6ffee");
+ assertEquals("\"caf\u00E9 c\u00F6ffee\"", builder.toString());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 7c61e9a..e863f8b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -21,7 +21,6 @@ import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
-import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -29,6 +28,8 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
+import static android.net.TrafficStats.KB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
@@ -101,10 +102,6 @@ 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;
@@ -256,41 +253,49 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
public void testPidForegroundCombined() throws Exception {
+ IdleFuture idle;
+
// push all uid into background
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
- waitUntilIdle();
+ idle.get();
assertFalse(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// push one of the shared pids into foreground
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
- waitUntilIdle();
+ idle.get();
assertTrue(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// and swap another uid into foreground
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
- waitUntilIdle();
+ idle.get();
assertFalse(mService.isUidForeground(UID_A));
assertTrue(mService.isUidForeground(UID_B));
// push both pid into foreground
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
- waitUntilIdle();
+ idle.get();
assertTrue(mService.isUidForeground(UID_A));
// pull one out, should still be foreground
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
- waitUntilIdle();
+ idle.get();
assertTrue(mService.isUidForeground(UID_A));
// pull final pid out, should now be background
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
- waitUntilIdle();
+ idle.get();
assertFalse(mService.isUidForeground(UID_A));
}
@@ -434,7 +439,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 5, 1024L, 1024L, SNOOZE_NEVER);
+ sTemplateWifi, 5, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@@ -445,7 +450,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 20, 1024L, 1024L, SNOOZE_NEVER);
+ sTemplateWifi, 20, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@@ -456,7 +461,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER);
+ sTemplateWifi, 30, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@@ -467,14 +472,14 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
final long expectedCycle = parseTime("2007-02-28T23:59:59.000Z");
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER);
+ sTemplateWifi, 30, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
public void testNextCycleSane() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER);
+ sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, false);
final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
// walk forwards, ensuring that cycle boundaries don't get stuck
@@ -489,7 +494,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
public void testLastCycleSane() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER);
+ sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, false);
final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
// walk backwards, ensuring that cycle boundaries look sane
@@ -547,7 +552,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
replay();
setNetworkPolicies(new NetworkPolicy(
- sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER));
+ sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
future.get();
verifyAndReset();
}
@@ -604,8 +609,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
future = expectMeteredIfacesChanged();
replay();
- setNetworkPolicies(new NetworkPolicy(
- sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER));
+ setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES,
+ 2 * MB_IN_BYTES, false));
future.get();
verifyAndReset();
}
@@ -697,13 +702,51 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
tagFuture = expectEnqueueNotification();
replay();
- mService.snoozePolicy(sTemplateWifi);
+ mService.snoozeLimit(sTemplateWifi);
assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get());
future.get();
verifyAndReset();
}
}
+ public void testMeteredNetworkWithoutLimit() throws Exception {
+ NetworkState[] state = null;
+ NetworkStats stats = null;
+ Future<Void> future;
+ Future<String> tagFuture;
+
+ final long TIME_FEB_15 = 1171497600000L;
+ final long TIME_MAR_10 = 1173484800000L;
+ final int CYCLE_DAY = 15;
+
+ setCurrentTimeMillis(TIME_MAR_10);
+
+ // bring up wifi network with metered policy
+ state = new NetworkState[] { buildWifi() };
+ stats = new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
+
+ {
+ expectCurrentTime();
+ 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, Long.MAX_VALUE);
+
+ expectClearNotifications();
+ future = expectMeteredIfacesChanged(TEST_IFACE);
+
+ replay();
+ setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, WARNING_DISABLED,
+ LIMIT_DISABLED, true));
+ future.get();
+ verifyAndReset();
+ }
+ }
+
private static long parseTime(String time) {
final Time result = new Time();
result.parse3339(time);
@@ -850,10 +893,10 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
/**
* Wait until {@link #mService} internal {@link Handler} is idle.
*/
- private void waitUntilIdle() throws Exception {
+ private IdleFuture expectIdle() {
final IdleFuture future = new IdleFuture();
mService.addIdleHandler(future);
- future.get();
+ return future;
}
private static void assertTimeEquals(long expected, long actual) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index fbc171b..daf2018 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -31,6 +31,7 @@ import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -39,6 +40,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
@@ -63,10 +65,12 @@ import android.os.INetworkManagementService;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.util.TrustedTime;
import com.android.server.net.NetworkStatsService;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
+import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
import org.easymock.Capture;
import org.easymock.EasyMock;
@@ -282,13 +286,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
verifyAndReset();
- // talk with zombie service to assert stats have gone; and assert that
- // we persisted them to file.
- expectDefaultSettings();
- replay();
- assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
- verifyAndReset();
-
assertStatsFilesExist(true);
// boot through serviceReady() again
@@ -319,6 +316,8 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
}
+ // TODO: simulate reboot to test bucket resize
+ @Suppress
public void testStatsBucketResize() throws Exception {
NetworkStatsHistory history = null;
@@ -602,7 +601,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10);
verifyAndReset();
-
}
public void testSummaryForAllUid() throws Exception {
@@ -755,11 +753,15 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L));
+
+ final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
final String[] tetherIfacePairs = new String[] { TEST_IFACE, "wlan0" };
- expectNetworkStatsPoll(tetherIfacePairs, new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, 0L));
+ final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, 0L);
+
+ expectNetworkStatsUidDetail(uidStats, tetherIfacePairs, tetherStats);
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
@@ -802,10 +804,15 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
mNetManager.setGlobalAlert(anyLong());
expectLastCall().atLeastOnce();
+
+ expect(mNetManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
}
private void expectNetworkState(NetworkState... state) throws Exception {
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+
+ final LinkProperties linkProp = state.length > 0 ? state[0].linkProperties : null;
+ expect(mConnManager.getActiveLinkProperties()).andReturn(linkProp).atLeastOnce();
}
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
@@ -813,23 +820,35 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
}
private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
+ expectNetworkStatsUidDetail(detail, new String[0], new NetworkStats(0L, 0));
+ }
+
+ private void expectNetworkStatsUidDetail(
+ NetworkStats detail, String[] tetherIfacePairs, NetworkStats tetherStats)
+ throws Exception {
expect(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL))).andReturn(detail).atLeastOnce();
+
+ // also include tethering details, since they are folded into UID
+ expect(mConnManager.getTetheredIfacePairs()).andReturn(tetherIfacePairs).atLeastOnce();
+ expect(mNetManager.getNetworkStatsTethering(aryEq(tetherIfacePairs)))
+ .andReturn(tetherStats).atLeastOnce();
}
private void expectDefaultSettings() throws Exception {
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
}
- private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory)
+ private void expectSettings(long persistBytes, long bucketDuration, long deleteAge)
throws Exception {
expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
- expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes();
- expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes();
- expect(mSettings.getNetworkMaxHistory()).andReturn(maxHistory).anyTimes();
- expect(mSettings.getUidBucketDuration()).andReturn(bucketDuration).anyTimes();
- expect(mSettings.getUidMaxHistory()).andReturn(maxHistory).anyTimes();
- expect(mSettings.getTagMaxHistory()).andReturn(maxHistory).anyTimes();
expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
+ expect(mSettings.getGlobalAlertBytes()).andReturn(MB_IN_BYTES).anyTimes();
+ expect(mSettings.getSampleEnabled()).andReturn(true).anyTimes();
+
+ final Config config = new Config(bucketDuration, persistBytes, deleteAge, deleteAge);
+ expect(mSettings.getDevConfig()).andReturn(config).anyTimes();
+ expect(mSettings.getUidConfig()).andReturn(config).anyTimes();
+ expect(mSettings.getUidTagConfig()).andReturn(config).anyTimes();
}
private void expectCurrentTime() throws Exception {
@@ -841,27 +860,16 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
}
private void expectNetworkStatsPoll() throws Exception {
- expectNetworkStatsPoll(new String[0], new NetworkStats(getElapsedRealtime(), 0));
- }
-
- private void expectNetworkStatsPoll(String[] tetherIfacePairs, NetworkStats tetherStats)
- throws Exception {
mNetManager.setGlobalAlert(anyLong());
expectLastCall().anyTimes();
- expect(mConnManager.getTetheredIfacePairs()).andReturn(tetherIfacePairs).anyTimes();
- expect(mNetManager.getNetworkStatsTethering(eq(tetherIfacePairs)))
- .andReturn(tetherStats).anyTimes();
}
private void assertStatsFilesExist(boolean exist) {
- final File networkFile = new File(mStatsDir, "netstats.bin");
- final File uidFile = new File(mStatsDir, "netstats_uid.bin");
+ final File basePath = new File(mStatsDir, "netstats");
if (exist) {
- assertTrue(networkFile.exists());
- assertTrue(uidFile.exists());
+ assertTrue(basePath.list().length > 0);
} else {
- assertFalse(networkFile.exists());
- assertFalse(uidFile.exists());
+ assertTrue(basePath.list().length == 0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
new file mode 100644
index 0000000..7f05f56
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.NetworkIdentity;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.frameworks.servicestests.R;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+/**
+ * Tests for {@link NetworkStatsCollection}.
+ */
+@MediumTest
+public class NetworkStatsCollectionTest extends AndroidTestCase {
+
+ private static final String TEST_FILE = "test.bin";
+ private static final String TEST_IMSI = "310260000000000";
+
+ public void testReadLegacyNetwork() throws Exception {
+ final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
+ stageFile(R.raw.netstats_v1, testFile);
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ collection.readLegacyNetwork(testFile);
+
+ // verify that history read correctly
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 636014522L, 709291L, 88037144L, 518820L);
+
+ // now export into a unified format
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ collection.write(new DataOutputStream(bos));
+
+ // clear structure completely
+ collection.reset();
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 0L, 0L, 0L, 0L);
+
+ // and read back into structure, verifying that totals are same
+ collection.read(new ByteArrayInputStream(bos.toByteArray()));
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 636014522L, 709291L, 88037144L, 518820L);
+ }
+
+ public void testReadLegacyUid() throws Exception {
+ final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
+ stageFile(R.raw.netstats_uid_v4, testFile);
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ collection.readLegacyUid(testFile, false);
+
+ // verify that history read correctly
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 637073904L, 711398L, 88342093L, 521006L);
+
+ // now export into a unified format
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ collection.write(new DataOutputStream(bos));
+
+ // clear structure completely
+ collection.reset();
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 0L, 0L, 0L, 0L);
+
+ // and read back into structure, verifying that totals are same
+ collection.read(new ByteArrayInputStream(bos.toByteArray()));
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 637073904L, 711398L, 88342093L, 521006L);
+ }
+
+ public void testReadLegacyUidTags() throws Exception {
+ final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
+ stageFile(R.raw.netstats_uid_v4, testFile);
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ collection.readLegacyUid(testFile, true);
+
+ // verify that history read correctly
+ assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
+ 77017831L, 100995L, 35436758L, 92344L);
+
+ // now export into a unified format
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ collection.write(new DataOutputStream(bos));
+
+ // clear structure completely
+ collection.reset();
+ assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
+ 0L, 0L, 0L, 0L);
+
+ // and read back into structure, verifying that totals are same
+ collection.read(new ByteArrayInputStream(bos.toByteArray()));
+ assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
+ 77017831L, 100995L, 35436758L, 92344L);
+ }
+
+ /**
+ * Copy a {@link Resources#openRawResource(int)} into {@link File} for
+ * testing purposes.
+ */
+ private void stageFile(int rawId, File file) throws Exception {
+ new File(file.getParent()).mkdirs();
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = getContext().getResources().openRawResource(rawId);
+ out = new FileOutputStream(file);
+ Streams.copy(in, out);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ public static NetworkIdentitySet buildWifiIdent() {
+ final NetworkIdentitySet set = new NetworkIdentitySet();
+ set.add(new NetworkIdentity(ConnectivityManager.TYPE_WIFI, 0, null, false));
+ return set;
+ }
+
+ private static void assertSummaryTotal(NetworkStatsCollection collection,
+ NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ final NetworkStats.Entry entry = collection.getSummary(
+ template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
+ assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
+ }
+
+ private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection,
+ NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ final NetworkStats.Entry entry = collection.getSummary(
+ template, Long.MIN_VALUE, Long.MAX_VALUE).getTotalIncludingTags(null);
+ assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
+ }
+
+ private static void assertEntry(
+ NetworkStats.Entry entry, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+ assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
+ assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+ assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+ }
+}