summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/Android.mk3
-rw-r--r--services/audioflinger/AudioDumpInterface.cpp220
-rw-r--r--services/audioflinger/AudioDumpInterface.h12
-rw-r--r--services/audioflinger/AudioFlinger.cpp2855
-rw-r--r--services/audioflinger/AudioFlinger.h442
-rw-r--r--services/audioflinger/AudioMixer.cpp694
-rw-r--r--services/audioflinger/AudioMixer.h50
-rw-r--r--services/audioflinger/AudioPolicyManagerBase.cpp271
-rw-r--r--services/audioflinger/AudioPolicyService.cpp133
-rw-r--r--services/audioflinger/AudioPolicyService.h47
-rw-r--r--services/camera/libcameraservice/Android.mk19
-rw-r--r--services/camera/libcameraservice/CameraHardwareStub.cpp60
-rw-r--r--services/camera/libcameraservice/CameraHardwareStub.h2
-rw-r--r--services/camera/libcameraservice/CameraService.cpp1771
-rw-r--r--services/camera/libcameraservice/CameraService.h275
-rw-r--r--services/camera/libcameraservice/FakeCamera.cpp25
-rw-r--r--services/camera/libcameraservice/FakeCamera.h2
-rw-r--r--services/camera/tests/CameraServiceTest/Android.mk4
-rw-r--r--services/camera/tests/CameraServiceTest/CameraServiceTest.cpp256
-rw-r--r--services/java/Android.mk4
-rw-r--r--services/java/com/android/server/AppWidgetService.java11
-rw-r--r--services/java/com/android/server/BackupManagerService.java6
-rw-r--r--services/java/com/android/server/BatteryService.java31
-rw-r--r--services/java/com/android/server/BootReceiver.java4
-rw-r--r--services/java/com/android/server/ConnectivityService.java113
-rw-r--r--services/java/com/android/server/DropBoxManagerService.java2
-rw-r--r--services/java/com/android/server/InputApplication.java (renamed from services/java/com/android/server/status/StatusBarException.java)22
-rw-r--r--services/java/com/android/server/InputDevice.java1025
-rw-r--r--services/java/com/android/server/InputManager.java574
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java35
-rw-r--r--services/java/com/android/server/InputWindow.java66
-rw-r--r--services/java/com/android/server/InputWindowList.java89
-rw-r--r--services/java/com/android/server/Installer.java49
-rw-r--r--services/java/com/android/server/KeyInputQueue.java1386
-rw-r--r--services/java/com/android/server/LocationManagerService.java354
-rw-r--r--services/java/com/android/server/MasterClearReceiver.java6
-rw-r--r--services/java/com/android/server/MountService.java577
-rw-r--r--services/java/com/android/server/NativeDaemonConnector.java11
-rw-r--r--services/java/com/android/server/NetworkManagementService.java348
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java151
-rw-r--r--services/java/com/android/server/PackageManagerService.java802
-rw-r--r--services/java/com/android/server/PowerManagerService.java165
-rw-r--r--services/java/com/android/server/SensorService.java282
-rw-r--r--services/java/com/android/server/StatusBarManagerService.java472
-rw-r--r--services/java/com/android/server/SystemServer.java39
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java14
-rw-r--r--services/java/com/android/server/UiModeManagerService.java2
-rw-r--r--services/java/com/android/server/UsbObserver.java207
-rw-r--r--services/java/com/android/server/ViewServer.java208
-rw-r--r--services/java/com/android/server/Watchdog.java413
-rw-r--r--services/java/com/android/server/WifiService.java75
-rw-r--r--services/java/com/android/server/WindowManagerService.java2851
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java5334
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java (renamed from services/java/com/android/server/am/HistoryRecord.java)119
-rw-r--r--services/java/com/android/server/am/ActivityResult.java4
-rw-r--r--services/java/com/android/server/am/ActivityStack.java3522
-rw-r--r--services/java/com/android/server/am/AppErrorDialog.java3
-rw-r--r--services/java/com/android/server/am/AppNotRespondingDialog.java2
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java31
-rw-r--r--services/java/com/android/server/am/BroadcastRecord.java68
-rw-r--r--services/java/com/android/server/am/ConnectionRecord.java4
-rw-r--r--services/java/com/android/server/am/ContentProviderRecord.java4
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java6
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java43
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java169
-rw-r--r--services/java/com/android/server/am/StrictModeViolationDialog.java98
-rw-r--r--services/java/com/android/server/am/UriPermission.java48
-rw-r--r--services/java/com/android/server/am/UriPermissionOwner.java166
-rw-r--r--services/java/com/android/server/am/UsageStatsService.java121
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java11
-rw-r--r--services/java/com/android/server/location/GeocoderProxy.java105
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java1473
-rw-r--r--services/java/com/android/server/location/GpsXtraDownloader.java171
-rw-r--r--services/java/com/android/server/location/LocationProviderInterface.java56
-rw-r--r--services/java/com/android/server/location/LocationProviderProxy.java423
-rw-r--r--services/java/com/android/server/location/MockProvider.java233
-rw-r--r--services/java/com/android/server/location/PassiveProvider.java152
-rw-r--r--services/java/com/android/server/sip/SipHelper.java452
-rw-r--r--services/java/com/android/server/sip/SipService.java1086
-rw-r--r--services/java/com/android/server/sip/SipSessionGroup.java1080
-rw-r--r--services/java/com/android/server/sip/SipSessionListenerProxy.java206
-rw-r--r--services/java/com/android/server/status/AnimatedImageView.java85
-rw-r--r--services/java/com/android/server/status/CloseDragHandle.java51
-rw-r--r--services/java/com/android/server/status/DateView.java89
-rw-r--r--services/java/com/android/server/status/ExpandedView.java58
-rw-r--r--services/java/com/android/server/status/FixedSizeDrawable.java66
-rw-r--r--services/java/com/android/server/status/IconData.java122
-rw-r--r--services/java/com/android/server/status/IconMerger.java133
-rw-r--r--services/java/com/android/server/status/LatestItemView.java34
-rw-r--r--services/java/com/android/server/status/NotificationData.java44
-rw-r--r--services/java/com/android/server/status/NotificationLinearLayout.java29
-rw-r--r--services/java/com/android/server/status/NotificationViewList.java276
-rw-r--r--services/java/com/android/server/status/StatusBarIcon.java186
-rw-r--r--services/java/com/android/server/status/StatusBarNotification.java27
-rw-r--r--services/java/com/android/server/status/StatusBarPolicy.java1390
-rw-r--r--services/java/com/android/server/status/StatusBarService.java1881
-rw-r--r--services/java/com/android/server/status/StatusBarView.java192
-rw-r--r--services/java/com/android/server/status/StorageNotification.java395
-rw-r--r--services/java/com/android/server/status/Ticker.java246
-rw-r--r--services/java/com/android/server/status/TickerView.java38
-rw-r--r--services/java/com/android/server/status/TrackingPatternView.java70
-rw-r--r--services/java/com/android/server/status/TrackingView.java63
-rw-r--r--services/java/com/android/server/status/UsbStorageActivity.java272
-rwxr-xr-xservices/java/com/android/server/status/package.html5
-rw-r--r--services/jni/Android.mk6
-rw-r--r--services/jni/com_android_server_InputManager.cpp2590
-rw-r--r--services/jni/com_android_server_KeyInputQueue.cpp358
-rw-r--r--services/jni/com_android_server_PowerManagerService.cpp169
-rw-r--r--services/jni/com_android_server_PowerManagerService.h44
-rw-r--r--services/jni/com_android_server_SensorService.cpp177
-rwxr-xr-xservices/jni/com_android_server_location_GpsLocationProvider.cpp467
-rw-r--r--services/jni/onload.cpp10
-rw-r--r--services/sensorservice/Android.mk28
-rw-r--r--services/sensorservice/SensorService.cpp560
-rw-r--r--services/sensorservice/SensorService.h141
-rw-r--r--services/sensorservice/tests/Android.mk14
-rw-r--r--services/sensorservice/tests/sensorservicetest.cpp90
-rw-r--r--services/surfaceflinger/Android.mk7
-rw-r--r--services/surfaceflinger/Barrier.h4
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.cpp146
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.h16
-rw-r--r--services/surfaceflinger/GLExtensions.cpp133
-rw-r--r--services/surfaceflinger/GLExtensions.h94
-rw-r--r--services/surfaceflinger/Layer.cpp778
-rw-r--r--services/surfaceflinger/Layer.h205
-rw-r--r--services/surfaceflinger/LayerBase.cpp486
-rw-r--r--services/surfaceflinger/LayerBase.h147
-rw-r--r--services/surfaceflinger/LayerBlur.cpp70
-rw-r--r--services/surfaceflinger/LayerBlur.h8
-rw-r--r--services/surfaceflinger/LayerBuffer.cpp91
-rw-r--r--services/surfaceflinger/LayerBuffer.h19
-rw-r--r--services/surfaceflinger/LayerDim.cpp85
-rw-r--r--services/surfaceflinger/LayerDim.h8
-rw-r--r--services/surfaceflinger/MessageQueue.cpp13
-rw-r--r--services/surfaceflinger/MessageQueue.h35
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp746
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h228
-rw-r--r--services/surfaceflinger/TextureManager.cpp341
-rw-r--r--services/surfaceflinger/TextureManager.h93
-rw-r--r--services/surfaceflinger/Tokenizer.cpp173
-rw-r--r--services/surfaceflinger/Tokenizer.h57
-rw-r--r--services/surfaceflinger/Transform.cpp7
-rw-r--r--services/surfaceflinger/Transform.h4
-rw-r--r--services/surfaceflinger/tests/surface/Android.mk18
-rw-r--r--services/surfaceflinger/tests/surface/surface.cpp54
-rw-r--r--services/tests/servicestests/src/com/android/server/DropBoxTest.java208
146 files changed, 27149 insertions, 20256 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 870c0b8..22ecc54 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -87,7 +87,8 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder \
libmedia \
- libhardware_legacy
+ libhardware_legacy \
+ libeffects
ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
diff --git a/services/audioflinger/AudioDumpInterface.cpp b/services/audioflinger/AudioDumpInterface.cpp
index a018b4c..6c11114 100644
--- a/services/audioflinger/AudioDumpInterface.cpp
+++ b/services/audioflinger/AudioDumpInterface.cpp
@@ -32,7 +32,7 @@ namespace android {
// ----------------------------------------------------------------------------
AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw)
- : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8(""))
+ : mPolicyCommands(String8("")), mFileName(String8(""))
{
if(hw == 0) {
LOGE("Dump construct hw = 0");
@@ -47,6 +47,11 @@ AudioDumpInterface::~AudioDumpInterface()
for (size_t i = 0; i < mOutputs.size(); i++) {
closeOutputStream((AudioStreamOut *)mOutputs[i]);
}
+
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ closeInputStream((AudioStreamIn *)mInputs[i]);
+ }
+
if(mFinalInterface) delete mFinalInterface;
}
@@ -60,31 +65,32 @@ AudioStreamOut* AudioDumpInterface::openOutputStream(
uint32_t lRate = 44100;
- if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices) || mFirstHwOutput) {
- outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status);
- if (outFinal != 0) {
- lFormat = outFinal->format();
- lChannels = outFinal->channels();
- lRate = outFinal->sampleRate();
- if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
- mFirstHwOutput = false;
- }
- }
+ outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status);
+ if (outFinal != 0) {
+ lFormat = outFinal->format();
+ lChannels = outFinal->channels();
+ lRate = outFinal->sampleRate();
} else {
- if (format != 0 && *format != 0) {
- lFormat = *format;
- } else {
- lFormat = AudioSystem::PCM_16_BIT;
+ if (format != 0) {
+ if (*format != 0) {
+ lFormat = *format;
+ } else {
+ *format = lFormat;
+ }
}
- if (channels != 0 && *channels != 0) {
- lChannels = *channels;
- } else {
- lChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ if (channels != 0) {
+ if (*channels != 0) {
+ lChannels = *channels;
+ } else {
+ *channels = lChannels;
+ }
}
- if (sampleRate != 0 && *sampleRate != 0) {
- lRate = *sampleRate;
- } else {
- lRate = 44100;
+ if (sampleRate != 0) {
+ if (*sampleRate != 0) {
+ lRate = *sampleRate;
+ } else {
+ *sampleRate = lRate;
+ }
}
if (status) *status = NO_ERROR;
}
@@ -111,7 +117,6 @@ void AudioDumpInterface::closeOutputStream(AudioStreamOut* out)
dumpOut->standby();
if (dumpOut->finalStream() != NULL) {
mFinalInterface->closeOutputStream(dumpOut->finalStream());
- mFirstHwOutput = true;
}
mOutputs.remove(dumpOut);
@@ -126,18 +131,33 @@ AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format
uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO;
uint32_t lRate = 8000;
-
- if (mInputs.size() == 0) {
- inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
- if (inFinal == 0) return 0;
-
+ inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
+ if (inFinal != 0) {
lFormat = inFinal->format();
lChannels = inFinal->channels();
lRate = inFinal->sampleRate();
} else {
- if (format != 0 && *format != 0) lFormat = *format;
- if (channels != 0 && *channels != 0) lChannels = *channels;
- if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate;
+ if (format != 0) {
+ if (*format != 0) {
+ lFormat = *format;
+ } else {
+ *format = lFormat;
+ }
+ }
+ if (channels != 0) {
+ if (*channels != 0) {
+ lChannels = *channels;
+ } else {
+ *channels = lChannels;
+ }
+ }
+ if (sampleRate != 0) {
+ if (*sampleRate != 0) {
+ lRate = *sampleRate;
+ } else {
+ *sampleRate = lRate;
+ }
+ }
if (status) *status = NO_ERROR;
}
LOGV("openInputStream(), inFinal %p", inFinal);
@@ -223,6 +243,15 @@ String8 AudioDumpInterface::getParameters(const String8& keys)
return keyValuePairs;
}
+status_t AudioDumpInterface::setMode(int mode)
+{
+ return mFinalInterface->setMode(mode);
+}
+
+size_t AudioDumpInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+{
+ return mFinalInterface->getInputBufferSize(sampleRate, format, channelCount);
+}
// ----------------------------------------------------------------------------
@@ -235,7 +264,7 @@ AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface,
uint32_t sampleRate)
: mInterface(interface), mId(id),
mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices),
- mBufferSize(1024), mFinalStream(finalStream), mOutFile(0), mFileCount(0)
+ mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0)
{
LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
}
@@ -254,26 +283,26 @@ ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes)
if (mFinalStream) {
ret = mFinalStream->write(buffer, bytes);
} else {
- usleep((bytes * 1000000) / frameSize() / sampleRate());
+ usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000);
ret = bytes;
}
- if(!mOutFile) {
+ if(!mFile) {
if (mInterface->fileName() != "") {
char name[255];
- sprintf(name, "%s_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
- mOutFile = fopen(name, "wb");
- LOGV("Opening dump file %s, fh %p", name, mOutFile);
+ sprintf(name, "%s_out_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
+ mFile = fopen(name, "wb");
+ LOGV("Opening dump file %s, fh %p", name, mFile);
}
}
- if (mOutFile) {
- fwrite(buffer, bytes, 1, mOutFile);
+ if (mFile) {
+ fwrite(buffer, bytes, 1, mFile);
}
return ret;
}
status_t AudioStreamOutDump::standby()
{
- LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream);
+ LOGV("AudioStreamOutDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream);
Close();
if (mFinalStream != 0 ) return mFinalStream->standby();
@@ -330,7 +359,7 @@ status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs)
}
if (param.getInt(String8("format"), valueInt) == NO_ERROR) {
- if (mOutFile == 0) {
+ if (mFile == 0) {
mFormat = valueInt;
} else {
status = INVALID_OPERATION;
@@ -345,7 +374,7 @@ status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs)
}
if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) {
if (valueInt > 0 && valueInt <= 48000) {
- if (mOutFile == 0) {
+ if (mFile == 0) {
mSampleRate = valueInt;
} else {
status = INVALID_OPERATION;
@@ -373,9 +402,9 @@ status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args)
void AudioStreamOutDump::Close()
{
- if(mOutFile) {
- fclose(mOutFile);
- mOutFile = 0;
+ if(mFile) {
+ fclose(mFile);
+ mFile = 0;
}
}
@@ -396,7 +425,7 @@ AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface,
uint32_t sampleRate)
: mInterface(interface), mId(id),
mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices),
- mBufferSize(1024), mFinalStream(finalStream), mInFile(0)
+ mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0)
{
LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
}
@@ -409,55 +438,68 @@ AudioStreamInDump::~AudioStreamInDump()
ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes)
{
- if (mFinalStream) {
- return mFinalStream->read(buffer, bytes);
- }
-
- usleep((bytes * 1000000) / frameSize() / sampleRate());
+ ssize_t ret;
- if(!mInFile) {
- char name[255];
- strcpy(name, "/sdcard/music/sine440");
- if (channels() == AudioSystem::CHANNEL_IN_MONO) {
- strcat(name, "_mo");
- } else {
- strcat(name, "_st");
+ if (mFinalStream) {
+ ret = mFinalStream->read(buffer, bytes);
+ if(!mFile) {
+ if (mInterface->fileName() != "") {
+ char name[255];
+ sprintf(name, "%s_in_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
+ mFile = fopen(name, "wb");
+ LOGV("Opening input dump file %s, fh %p", name, mFile);
+ }
}
- if (format() == AudioSystem::PCM_16_BIT) {
- strcat(name, "_16b");
- } else {
- strcat(name, "_8b");
+ if (mFile) {
+ fwrite(buffer, bytes, 1, mFile);
}
- if (sampleRate() < 16000) {
- strcat(name, "_8k");
- } else if (sampleRate() < 32000) {
- strcat(name, "_22k");
- } else if (sampleRate() < 48000) {
- strcat(name, "_44k");
- } else {
- strcat(name, "_48k");
- }
- strcat(name, ".wav");
- mInFile = fopen(name, "rb");
- LOGV("Opening dump file %s, fh %p", name, mInFile);
- if (mInFile) {
- fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
+ } else {
+ usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000);
+ ret = bytes;
+ if(!mFile) {
+ char name[255];
+ strcpy(name, "/sdcard/music/sine440");
+ if (channels() == AudioSystem::CHANNEL_IN_MONO) {
+ strcat(name, "_mo");
+ } else {
+ strcat(name, "_st");
+ }
+ if (format() == AudioSystem::PCM_16_BIT) {
+ strcat(name, "_16b");
+ } else {
+ strcat(name, "_8b");
+ }
+ if (sampleRate() < 16000) {
+ strcat(name, "_8k");
+ } else if (sampleRate() < 32000) {
+ strcat(name, "_22k");
+ } else if (sampleRate() < 48000) {
+ strcat(name, "_44k");
+ } else {
+ strcat(name, "_48k");
+ }
+ strcat(name, ".wav");
+ mFile = fopen(name, "rb");
+ LOGV("Opening input read file %s, fh %p", name, mFile);
+ if (mFile) {
+ fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
+ }
}
-
- }
- if (mInFile) {
- ssize_t bytesRead = fread(buffer, bytes, 1, mInFile);
- if (bytesRead != bytes) {
- fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
- fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mInFile);
+ if (mFile) {
+ ssize_t bytesRead = fread(buffer, bytes, 1, mFile);
+ if (bytesRead >=0 && bytesRead < bytes) {
+ fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
+ fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mFile);
+ }
}
}
- return bytes;
+
+ return ret;
}
status_t AudioStreamInDump::standby()
{
- LOGV("AudioStreamInDump standby(), mInFile %p, mFinalStream %p", mInFile, mFinalStream);
+ LOGV("AudioStreamInDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream);
Close();
if (mFinalStream != 0 ) return mFinalStream->standby();
@@ -523,9 +565,9 @@ status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args)
void AudioStreamInDump::Close()
{
- if(mInFile) {
- fclose(mInFile);
- mInFile = 0;
+ if(mFile) {
+ fclose(mFile);
+ mFile = 0;
}
}
}; // namespace android
diff --git a/services/audioflinger/AudioDumpInterface.h b/services/audioflinger/AudioDumpInterface.h
index 4c62b3e..814ce5f 100644
--- a/services/audioflinger/AudioDumpInterface.h
+++ b/services/audioflinger/AudioDumpInterface.h
@@ -69,7 +69,7 @@ private:
uint32_t mDevice; // current device this output is routed to
size_t mBufferSize;
AudioStreamOut *mFinalStream;
- FILE *mOutFile; // output file
+ FILE *mFile; // output file
int mFileCount;
};
@@ -109,7 +109,8 @@ private:
uint32_t mDevice; // current device this output is routed to
size_t mBufferSize;
AudioStreamIn *mFinalStream;
- FILE *mInFile; // output file
+ FILE *mFile; // output file
+ int mFileCount;
};
class AudioDumpInterface : public AudioHardwareBase
@@ -134,6 +135,8 @@ public:
virtual status_t setMasterVolume(float volume)
{return mFinalInterface->setMasterVolume(volume);}
+ virtual status_t setMode(int mode);
+
// mic mute
virtual status_t setMicMute(bool state)
{return mFinalInterface->setMicMute(state);}
@@ -143,6 +146,8 @@ public:
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys);
+ virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
+
virtual AudioStreamIn* openInputStream(uint32_t devices, int *format, uint32_t *channels,
uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics);
virtual void closeInputStream(AudioStreamIn* in);
@@ -153,8 +158,7 @@ public:
protected:
AudioHardwareInterface *mFinalInterface;
- SortedVector<AudioStreamOutDump *> mOutputs;
- bool mFirstHwOutput;
+ SortedVector<AudioStreamOutDump *> mOutputs;
SortedVector<AudioStreamInDump *> mInputs;
Mutex mLock;
String8 mPolicyCommands;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2414e8d..ff31470 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -37,7 +37,7 @@
#include <media/AudioRecord.h>
#include <private/media/AudioTrackShared.h>
-
+#include <private/media/AudioEffectShared.h>
#include <hardware_legacy/AudioHardwareInterface.h>
#include "AudioMixer.h"
@@ -51,6 +51,9 @@
#include "lifevibes.h"
#endif
+#include <media/EffectsFactoryApi.h>
+#include <media/EffectVisualizerApi.h>
+
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
@@ -60,6 +63,8 @@
// ----------------------------------------------------------------------------
+extern const char * const gEffectLibPath;
+
namespace android {
static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n";
@@ -67,6 +72,7 @@ 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;
// retry counts for buffer fill timeout
// 50 * ~20msecs = 1 second
@@ -123,7 +129,7 @@ static bool settingsAllowed() {
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0)
+ mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
{
mHardwareStatus = AUDIO_HW_IDLE;
@@ -132,8 +138,8 @@ AudioFlinger::AudioFlinger()
mHardwareStatus = AUDIO_HW_INIT;
if (mAudioHardware->initCheck() == NO_ERROR) {
// open 16-bit output stream for s/w mixer
-
- setMode(AudioSystem::MODE_NORMAL);
+ mMode = AudioSystem::MODE_NORMAL;
+ setMode(mMode);
setMasterVolume(1.0f);
setMasterMute(false);
@@ -142,6 +148,7 @@ AudioFlinger::AudioFlinger()
}
#ifdef LVMX
LifeVibes::init();
+ mLifeVibesClientPid = -1;
#endif
}
@@ -281,6 +288,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
uint32_t flags,
const sp<IMemory>& sharedBuffer,
int output,
+ int *sessionId,
status_t *status)
{
sp<PlaybackThread::Track> track;
@@ -288,6 +296,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
sp<Client> client;
wp<Client> wclient;
status_t lStatus;
+ int lSessionId;
if (streamType >= AudioSystem::NUM_STREAM_TYPES) {
LOGE("invalid stream type");
@@ -298,6 +307,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
{
Mutex::Autolock _l(mLock);
PlaybackThread *thread = checkPlaybackThread_l(output);
+ PlaybackThread *effectThread = NULL;
if (thread == NULL) {
LOGE("unknown output thread");
lStatus = BAD_VALUE;
@@ -312,8 +322,44 @@ sp<IAudioTrack> AudioFlinger::createTrack(
client = new Client(this, pid);
mClients.add(pid, client);
}
+
+ LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
+ if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ if (mPlaybackThreads.keyAt(i) != output) {
+ // prevent same audio session on different output threads
+ uint32_t sessions = t->hasAudioSession(*sessionId);
+ if (sessions & PlaybackThread::TRACK_SESSION) {
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ // check if an effect with same session ID is waiting for a track to be created
+ if (sessions & PlaybackThread::EFFECT_SESSION) {
+ effectThread = t.get();
+ }
+ }
+ }
+ lSessionId = *sessionId;
+ } else {
+ // if no audio session id is provided, create one here
+ lSessionId = nextUniqueId();
+ if (sessionId != NULL) {
+ *sessionId = lSessionId;
+ }
+ }
+ LOGV("createTrack() lSessionId: %d", lSessionId);
+
track = thread->createTrack_l(client, streamType, sampleRate, format,
- channelCount, frameCount, sharedBuffer, &lStatus);
+ channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
+
+ // move effect chain to this output thread if an effect on same session was waiting
+ // for a track to be created
+ if (lStatus == NO_ERROR && effectThread != NULL) {
+ Mutex::Autolock _dl(thread->mLock);
+ Mutex::Autolock _sl(effectThread->mLock);
+ moveEffectChain_l(lSessionId, effectThread, thread, true);
+ }
}
if (lStatus == NO_ERROR) {
trackHandle = new TrackHandle(track);
@@ -410,6 +456,8 @@ status_t AudioFlinger::setMasterVolume(float value)
status_t AudioFlinger::setMode(int mode)
{
+ status_t ret;
+
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
@@ -419,15 +467,23 @@ status_t AudioFlinger::setMode(int mode)
return BAD_VALUE;
}
- AutoMutex lock(mHardwareLock);
- mHardwareStatus = AUDIO_HW_SET_MODE;
- status_t ret = mAudioHardware->setMode(mode);
-#ifdef LVMX
+ { // scope for the lock
+ AutoMutex lock(mHardwareLock);
+ mHardwareStatus = AUDIO_HW_SET_MODE;
+ ret = mAudioHardware->setMode(mode);
+ mHardwareStatus = AUDIO_HW_IDLE;
+ }
+
if (NO_ERROR == ret) {
+ Mutex::Autolock _l(mLock);
+ mMode = mode;
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setMode(mode);
+#ifdef LVMX
LifeVibes::setMode(mode);
- }
#endif
- mHardwareStatus = AUDIO_HW_IDLE;
+ }
+
return ret;
}
@@ -596,8 +652,10 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
int musicEnabled = -1;
if (NO_ERROR == param.get(key, value)) {
if (value == LifevibesEnable) {
+ mLifeVibesClientPid = IPCThreadState::self()->getCallingPid();
musicEnabled = 1;
} else if (value == LifevibesDisable) {
+ mLifeVibesClientPid = -1;
musicEnabled = 0;
}
}
@@ -609,7 +667,7 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
mHardwareStatus = AUDIO_SET_PARAMETER;
result = mAudioHardware->setParameters(keyValuePairs);
#ifdef LVMX
- if ((NO_ERROR == result) && (musicEnabled != -1)) {
+ if (musicEnabled != -1) {
LifeVibes::enableMusic((bool) musicEnabled);
}
#endif
@@ -713,51 +771,57 @@ status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrame
void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
{
- LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid());
Mutex::Autolock _l(mLock);
- sp<IBinder> binder = client->asBinder();
- if (mNotificationClients.indexOf(binder) < 0) {
- LOGV("Adding notification client %p", binder.get());
- binder->linkToDeath(this);
- mNotificationClients.add(binder);
- }
+ int pid = IPCThreadState::self()->getCallingPid();
+ if (mNotificationClients.indexOfKey(pid) < 0) {
+ sp<NotificationClient> notificationClient = new NotificationClient(this,
+ client,
+ pid);
+ LOGV("registerClient() client %p, pid %d", notificationClient.get(), pid);
- // the config change is always sent from playback or record threads to avoid deadlock
- // with AudioSystem::gLock
- for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED);
- }
+ mNotificationClients.add(pid, notificationClient);
+
+ sp<IBinder> binder = client->asBinder();
+ binder->linkToDeath(notificationClient);
- for (size_t i = 0; i < mRecordThreads.size(); i++) {
- mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED);
+ // the config change is always sent from playback or record threads to avoid deadlock
+ // with AudioSystem::gLock
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED);
+ }
+
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED);
+ }
}
}
-void AudioFlinger::binderDied(const wp<IBinder>& who) {
-
- LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid());
+void AudioFlinger::removeNotificationClient(pid_t pid)
+{
Mutex::Autolock _l(mLock);
- IBinder *binder = who.unsafe_get();
-
- if (binder != NULL) {
- int index = mNotificationClients.indexOf(binder);
- if (index >= 0) {
- LOGV("Removing notification client %p", binder);
- mNotificationClients.removeAt(index);
+ int index = mNotificationClients.indexOfKey(pid);
+ if (index >= 0) {
+ sp <NotificationClient> client = mNotificationClients.valueFor(pid);
+ LOGV("removeNotificationClient() %p, pid %d", client.get(), pid);
+#ifdef LVMX
+ if (pid == mLifeVibesClientPid) {
+ LOGV("Disabling lifevibes");
+ LifeVibes::enableMusic(false);
+ mLifeVibesClientPid = -1;
}
+#endif
+ mNotificationClients.removeItem(pid);
}
}
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2) {
+void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2)
+{
size_t size = mNotificationClients.size();
for (size_t i = 0; i < size; i++) {
- sp<IBinder> binder = mNotificationClients.itemAt(i);
- LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get());
- sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
- client->ioConfigChanged(event, ioHandle, param2);
+ mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2);
}
}
@@ -768,12 +832,13 @@ void AudioFlinger::removeClient_l(pid_t pid)
mClients.removeItem(pid);
}
+
// ----------------------------------------------------------------------------
AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id)
: Thread(false),
mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
- mFormat(0), mFrameSize(1), mStandby(false), mId(id), mExiting(false)
+ mFrameSize(1), mFormat(0), mStandby(false), mId(id), mExiting(false)
{
}
@@ -806,7 +871,7 @@ uint32_t AudioFlinger::ThreadBase::sampleRate() const
int AudioFlinger::ThreadBase::channelCount() const
{
- return mChannelCount;
+ return (int)mChannelCount;
}
int AudioFlinger::ThreadBase::format() const
@@ -863,11 +928,12 @@ void AudioFlinger::ThreadBase::processConfigEvents()
LOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
ConfigEvent *configEvent = mConfigEvents[0];
mConfigEvents.removeAt(0);
- // release mLock because audioConfigChanged() will lock AudioFlinger mLock
- // before calling Audioflinger::audioConfigChanged_l() thus creating
- // potential cross deadlock between AudioFlinger::mLock and mLock
+ // release mLock before locking AudioFlinger mLock: lock order is always
+ // AudioFlinger then ThreadBase to avoid cross deadlock
mLock.unlock();
- audioConfigChanged(configEvent->mEvent, configEvent->mParam);
+ mAudioFlinger->mLock.lock();
+ audioConfigChanged_l(configEvent->mEvent, configEvent->mParam);
+ mAudioFlinger->mLock.unlock();
delete configEvent;
mLock.lock();
}
@@ -929,10 +995,11 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args
// ----------------------------------------------------------------------------
-AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
: ThreadBase(audioFlinger, id),
mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output),
- mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
+ mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
+ mDevice(device)
{
readOutputParameters();
@@ -943,8 +1010,6 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
}
- // notify client processes that a new input has been opened
- sendConfigEvent(AudioSystem::OUTPUT_OPENED);
}
AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -956,6 +1021,7 @@ status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args
{
dumpInternals(fd, args);
dumpTracks(fd, args);
+ dumpEffectChains(fd, args);
return NO_ERROR;
}
@@ -967,7 +1033,7 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>
snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
result.append(buffer);
- result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n");
+ result.append(" Name Clien Typ Fmt Chn Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n");
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (track != 0) {
@@ -978,7 +1044,7 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>
snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
result.append(buffer);
- result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n");
+ result.append(" Name Clien Typ Fmt Chn 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) {
@@ -993,6 +1059,24 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>
return NO_ERROR;
}
+status_t AudioFlinger::PlaybackThread::dumpEffectChains(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\n- %d Effect Chains:\n", mEffectChains.size());
+ write(fd, buffer, strlen(buffer));
+
+ for (size_t i = 0; i < mEffectChains.size(); ++i) {
+ sp<EffectChain> chain = mEffectChains[i];
+ if (chain != 0) {
+ chain->dump(fd, args);
+ }
+ }
+ return NO_ERROR;
+}
+
status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
@@ -1011,6 +1095,8 @@ status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String
result.append(buffer);
snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended);
result.append(buffer);
+ snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer);
+ result.append(buffer);
write(fd, result.string(), result.size());
dumpBase(fd, args);
@@ -1048,13 +1134,14 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra
int channelCount,
int frameCount,
const sp<IMemory>& sharedBuffer,
+ int sessionId,
status_t *status)
{
sp<Track> track;
status_t lStatus;
if (mType == DIRECT) {
- if (sampleRate != mSampleRate || format != mFormat || channelCount != mChannelCount) {
+ if (sampleRate != mSampleRate || format != mFormat || channelCount != (int)mChannelCount) {
LOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelCount %d for output %p",
sampleRate, format, channelCount, mOutput);
lStatus = BAD_VALUE;
@@ -1077,13 +1164,37 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra
{ // scope for mLock
Mutex::Autolock _l(mLock);
+
+ // all tracks in same audio session must share the same routing strategy otherwise
+ // conflicts will happen when tracks are moved from one output to another by audio policy
+ // manager
+ uint32_t strategy =
+ AudioSystem::getStrategyForStream((AudioSystem::stream_type)streamType);
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> t = mTracks[i];
+ if (t != 0) {
+ if (sessionId == t->sessionId() &&
+ strategy != AudioSystem::getStrategyForStream((AudioSystem::stream_type)t->type())) {
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
+ }
+
track = new Track(this, client, streamType, sampleRate, format,
- channelCount, frameCount, sharedBuffer);
+ channelCount, frameCount, sharedBuffer, sessionId);
if (track->getCblk() == NULL || track->name() < 0) {
lStatus = NO_MEMORY;
goto Exit;
}
mTracks.add(track);
+
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain != 0) {
+ LOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
+ track->setMainBuffer(chain->inBuffer());
+ chain->setStrategy(AudioSystem::getStrategyForStream((AudioSystem::stream_type)track->type()));
+ }
}
lStatus = NO_ERROR;
@@ -1200,6 +1311,14 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
track->mFillingUpStatus = Track::FS_FILLING;
track->mResetDone = false;
mActiveTracks.add(track);
+ if (track->mainBuffer() != mMixBuffer) {
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ LOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId());
+ chain->startTrack();
+ }
+ }
+
status = NO_ERROR;
}
@@ -1224,16 +1343,17 @@ String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
return mOutput->getParameters(keys);
}
-void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) {
+// destroyTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
AudioSystem::OutputDescriptor desc;
void *param2 = 0;
- LOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event, param);
+ LOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param);
switch (event) {
case AudioSystem::OUTPUT_OPENED:
case AudioSystem::OUTPUT_CONFIG_CHANGED:
- desc.channels = mChannelCount;
+ desc.channels = mChannels;
desc.samplingRate = mSampleRate;
desc.format = mFormat;
desc.frameCount = mFrameCount;
@@ -1247,24 +1367,34 @@ void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) {
default:
break;
}
- Mutex::Autolock _l(mAudioFlinger->mLock);
mAudioFlinger->audioConfigChanged_l(event, mId, param2);
}
void AudioFlinger::PlaybackThread::readOutputParameters()
{
mSampleRate = mOutput->sampleRate();
- mChannelCount = AudioSystem::popCount(mOutput->channels());
-
+ mChannels = mOutput->channels();
+ mChannelCount = (uint16_t)AudioSystem::popCount(mChannels);
mFormat = mOutput->format();
- mFrameSize = mOutput->frameSize();
+ mFrameSize = (uint16_t)mOutput->frameSize();
mFrameCount = mOutput->bufferSize() / 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;
+ if (mMixBuffer != NULL) delete[] mMixBuffer;
mMixBuffer = new int16_t[mFrameCount * 2];
memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
+
+ // force reconfiguration of effect chains and engines to take new buffer size and audio
+ // parameters into account
+ // Note that mLock is not held when readOutputParameters() is called from the constructor
+ // but in this case nothing is done below as no audio sessions have effect yet so it doesn't
+ // matter.
+ // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains
+ Vector< sp<EffectChain> > effectChains = mEffectChains;
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false);
+ }
}
status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
@@ -1280,10 +1410,76 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui
return mOutput->getRenderPosition(dspFrames);
}
+uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
+{
+ Mutex::Autolock _l(mLock);
+ uint32_t result = 0;
+ if (getEffectChain_l(sessionId) != 0) {
+ result = EFFECT_SESSION;
+ }
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (sessionId == track->sessionId() &&
+ !(track->mCblk->flags & CBLK_INVALID_MSK)) {
+ result |= TRACK_SESSION;
+ break;
+ }
+ }
+
+ return result;
+}
+
+uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
+{
+ // session AudioSystem::SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
+ // it is moved to correct output by audio policy manager when A2DP is connected or disconnected
+ if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+ return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+ }
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<Track> track = mTracks[i];
+ if (sessionId == track->sessionId() &&
+ !(track->mCblk->flags & CBLK_INVALID_MSK)) {
+ return AudioSystem::getStrategyForStream((AudioSystem::stream_type) track->type());
+ }
+ }
+ return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId)
+{
+ Mutex::Autolock _l(mLock);
+ return getEffectChain_l(sessionId);
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain_l(int sessionId)
+{
+ sp<EffectChain> chain;
+
+ size_t size = mEffectChains.size();
+ for (size_t i = 0; i < size; i++) {
+ if (mEffectChains[i]->sessionId() == sessionId) {
+ chain = mEffectChains[i];
+ break;
+ }
+ }
+ return chain;
+}
+
+void AudioFlinger::PlaybackThread::setMode(uint32_t mode)
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mEffectChains.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffectChains[i]->setMode_l(mode);
+ }
+}
+
// ----------------------------------------------------------------------------
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
- : PlaybackThread(audioFlinger, output, id),
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+ : PlaybackThread(audioFlinger, output, id, device),
mAudioMixer(0)
{
mType = PlaybackThread::MIXER;
@@ -1302,7 +1498,6 @@ AudioFlinger::MixerThread::~MixerThread()
bool AudioFlinger::MixerThread::threadLoop()
{
- int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
uint32_t mixerStatus = MIXER_IDLE;
nsecs_t standbyTime = systemTime();
@@ -1315,6 +1510,7 @@ bool AudioFlinger::MixerThread::threadLoop()
uint32_t activeSleepTime = activeSleepTimeUs();
uint32_t idleSleepTime = idleSleepTimeUs();
uint32_t sleepTime = idleSleepTime;
+ Vector< sp<EffectChain> > effectChains;
while (!exitPending())
{
@@ -1373,13 +1569,19 @@ bool AudioFlinger::MixerThread::threadLoop()
}
mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+
+ // prevent any changes in effect chain list and in each effect chain
+ // 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)) {
// mix buffers...
- mAudioMixer->process(curBuf);
+ mAudioMixer->process();
sleepTime = 0;
standbyTime = systemTime() + kStandbyTimeInNsecs;
+ //TODO: delay standby when effects have a tail
} else {
// If no tracks are ready, sleep once for the duration of an output
// buffer size, then write 0s to the output
@@ -1391,27 +1593,34 @@ bool AudioFlinger::MixerThread::threadLoop()
}
} else if (mBytesWritten != 0 ||
(mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
- memset (curBuf, 0, mixBufferSize);
+ memset (mMixBuffer, 0, mixBufferSize);
sleepTime = 0;
LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
}
+ // TODO add standby time extension fct of effect tail
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
- mLastWriteTime = systemTime();
- mInWrite = true;
- mBytesWritten += mixBufferSize;
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ // enable changes in effect chain
+ unlockEffectChains(effectChains);
#ifdef LVMX
int audioOutputType = LifeVibes::getMixerType(mId, mType);
if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
- LifeVibes::process(audioOutputType, curBuf, mixBufferSize);
+ LifeVibes::process(audioOutputType, mMixBuffer, mixBufferSize);
}
#endif
- int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
+ mLastWriteTime = systemTime();
+ mInWrite = true;
+ mBytesWritten += mixBufferSize;
+
+ int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
@@ -1430,6 +1639,8 @@ bool AudioFlinger::MixerThread::threadLoop()
}
mStandby = false;
} else {
+ // enable changes in effect chain
+ unlockEffectChains(effectChains);
usleep(sleepTime);
}
@@ -1437,6 +1648,10 @@ bool AudioFlinger::MixerThread::threadLoop()
// since we can't guarantee the destructors won't acquire that
// same lock.
tracksToRemove.clear();
+
+ // Effect chains will be actually deleted here if they were removed from
+ // mEffectChains list during mixing or effects processing
+ effectChains.clear();
}
if (!mStandby) {
@@ -1454,10 +1669,15 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
uint32_t mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
size_t count = activeTracks.size();
+ size_t mixedTracks = 0;
+ size_t tracksWithEffect = 0;
float masterVolume = mMasterVolume;
bool masterMute = mMasterMute;
+ if (masterMute) {
+ masterVolume = 0;
+ }
#ifdef LVMX
bool tracksConnectedChanged = false;
bool stateChanged = false;
@@ -1476,6 +1696,14 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute);
}
#endif
+ // Delegate master volume control to effect in output mix effect chain if needed
+ sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX);
+ if (chain != 0) {
+ uint32_t v = (uint32_t)(masterVolume * (1 << 24));
+ chain->setVolume_l(&v, &v);
+ masterVolume = (float)((v + (1 << 23)) >> 24);
+ chain.clear();
+ }
for (size_t i=0 ; i<count ; i++) {
sp<Track> t = activeTracks[i].promote();
@@ -1492,11 +1720,42 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
{
//LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this);
+ mixedTracks++;
+
+ // track->mainBuffer() != mMixBuffer means there is an effect chain
+ // connected to the track
+ chain.clear();
+ if (track->mainBuffer() != mMixBuffer) {
+ chain = getEffectChain_l(track->sessionId());
+ // Delegate volume control to effect in track effect chain if needed
+ if (chain != 0) {
+ tracksWithEffect++;
+ } else {
+ LOGW("prepareTracks_l(): track %08x attached to effect but no chain found on session %d",
+ track->name(), track->sessionId());
+ }
+ }
+
+
+ int param = AudioMixer::VOLUME;
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ // no ramp for the first volume setting
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ param = AudioMixer::RAMP_VOLUME;
+ }
+ } else if (cblk->server != 0) {
+ // If the track is stopped before the first frame was mixed,
+ // do not apply ramp
+ param = AudioMixer::RAMP_VOLUME;
+ }
+
// compute volume for this track
- int16_t left, right;
- if (track->isMuted() || masterMute || track->isPausing() ||
+ int16_t left, right, aux;
+ if (track->isMuted() || track->isPausing() ||
mStreamTypes[track->type()].mute) {
- left = right = 0;
+ left = right = aux = 0;
if (track->isPausing()) {
track->setPaused();
}
@@ -1515,31 +1774,28 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
}
#endif
float v = masterVolume * typeVolume;
- float v_clamped = v * cblk->volume[0];
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ uint32_t vl = (uint32_t)(v * cblk->volume[0]) << 12;
+ uint32_t vr = (uint32_t)(v * cblk->volume[1]) << 12;
+
+ // Delegate volume control to effect in track effect chain if needed
+ if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
+ // Do not ramp volume is volume is controlled by effect
+ param = AudioMixer::VOLUME;
+ }
+
+ // Convert volumes from 8.24 to 4.12 format
+ uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
left = int16_t(v_clamped);
- v_clamped = v * cblk->volume[1];
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ v_clamped = (vr + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
right = int16_t(v_clamped);
- }
- // XXX: these things DON'T need to be done each time
- mAudioMixer->setBufferProvider(track);
- mAudioMixer->enable(AudioMixer::MIXING);
-
- int param = AudioMixer::VOLUME;
- if (track->mFillingUpStatus == Track::FS_FILLED) {
- // no ramp for the first volume setting
- track->mFillingUpStatus = Track::FS_ACTIVE;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
- param = AudioMixer::RAMP_VOLUME;
- }
- } else if (cblk->server != 0) {
- // If the track is stopped before the first frame was mixed,
- // do not apply ramp
- param = AudioMixer::RAMP_VOLUME;
+ v_clamped = (uint32_t)(v * cblk->sendLevel);
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ aux = int16_t(v_clamped);
}
+
#ifdef LVMX
if ( tracksConnectedChanged || stateChanged )
{
@@ -1547,18 +1803,30 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
param = AudioMixer::VOLUME;
}
#endif
- mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left);
- mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right);
+
+ // XXX: these things DON'T need to be done each time
+ mAudioMixer->setBufferProvider(track);
+ mAudioMixer->enable(AudioMixer::MIXING);
+
+ mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left);
+ mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right);
+ mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux);
mAudioMixer->setParameter(
AudioMixer::TRACK,
- AudioMixer::FORMAT, track->format());
+ AudioMixer::FORMAT, (void *)track->format());
mAudioMixer->setParameter(
AudioMixer::TRACK,
- AudioMixer::CHANNEL_COUNT, track->channelCount());
+ AudioMixer::CHANNEL_COUNT, (void *)track->channelCount());
mAudioMixer->setParameter(
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
- int(cblk->sampleRate));
+ (void *)(cblk->sampleRate));
+ mAudioMixer->setParameter(
+ AudioMixer::TRACK,
+ AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
+ mAudioMixer->setParameter(
+ AudioMixer::TRACK,
+ AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
// reset retry count
track->mRetryCount = kMaxTrackRetries;
@@ -1572,7 +1840,6 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// We have consumed all the buffers of this track.
// Remove it from the list of active tracks.
tracksToRemove->add(track);
- mAudioMixer->disable(AudioMixer::MIXING);
} else {
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
@@ -1582,9 +1849,8 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
} else if (mixerStatus != MIXER_TRACKS_READY) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
-
- mAudioMixer->disable(AudioMixer::MIXING);
}
+ mAudioMixer->disable(AudioMixer::MIXING);
}
}
@@ -1594,6 +1860,13 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove->itemAt(i);
mActiveTracks.remove(track);
+ if (track->mainBuffer() != mMixBuffer) {
+ chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ LOGV("stopping track on chain %p for session Id: %d", chain.get(), track->sessionId());
+ chain->stopTrack();
+ }
+ }
if (track->isTerminated()) {
mTracks.remove(track);
deleteTrackName_l(track->mName);
@@ -1601,69 +1874,34 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
}
}
+ // mix buffer must be cleared if all tracks are connected to an
+ // effect chain as in this case the mixer will not write to
+ // mix buffer and track effects will accumulate into it
+ if (mixedTracks != 0 && mixedTracks == tracksWithEffect) {
+ memset(mMixBuffer, 0, mFrameCount * mChannelCount * sizeof(int16_t));
+ }
+
return mixerStatus;
}
-void AudioFlinger::MixerThread::getTracks(
- SortedVector < sp<Track> >& tracks,
- SortedVector < wp<Track> >& activeTracks,
- int streamType)
+void AudioFlinger::MixerThread::invalidateTracks(int streamType)
{
- LOGV ("MixerThread::getTracks() mixer %p, mTracks.size %d, mActiveTracks.size %d", this, mTracks.size(), mActiveTracks.size());
+ LOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
+ this, streamType, mTracks.size());
Mutex::Autolock _l(mLock);
+
size_t size = mTracks.size();
for (size_t i = 0; i < size; i++) {
sp<Track> t = mTracks[i];
if (t->type() == streamType) {
- tracks.add(t);
- int j = mActiveTracks.indexOf(t);
- if (j >= 0) {
- t = mActiveTracks[j].promote();
- if (t != NULL) {
- activeTracks.add(t);
- }
- }
+ t->mCblk->lock.lock();
+ t->mCblk->flags |= CBLK_INVALID_ON;
+ t->mCblk->cv.signal();
+ t->mCblk->lock.unlock();
}
}
-
- size = activeTracks.size();
- for (size_t i = 0; i < size; i++) {
- mActiveTracks.remove(activeTracks[i]);
- }
-
- size = tracks.size();
- for (size_t i = 0; i < size; i++) {
- sp<Track> t = tracks[i];
- mTracks.remove(t);
- deleteTrackName_l(t->name());
- }
}
-void AudioFlinger::MixerThread::putTracks(
- SortedVector < sp<Track> >& tracks,
- SortedVector < wp<Track> >& activeTracks)
-{
- LOGV ("MixerThread::putTracks() mixer %p, tracks.size %d, activeTracks.size %d", this, tracks.size(), activeTracks.size());
- Mutex::Autolock _l(mLock);
- size_t size = tracks.size();
- for (size_t i = 0; i < size ; i++) {
- sp<Track> t = tracks[i];
- int name = getTrackName_l();
-
- if (name < 0) return;
-
- t->mName = name;
- t->mThread = this;
- mTracks.add(t);
-
- int j = activeTracks.indexOf(t);
- if (j >= 0) {
- mActiveTracks.add(t);
- // force buffer refilling and no ramp volume when the track is mixed for the first time
- t->mFillingUpStatus = Track::FS_FILLING;
- }
- }
-}
// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::MixerThread::getTrackName_l()
@@ -1716,6 +1954,15 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
reconfig = true;
}
}
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ mDevice = (uint32_t)value;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(mDevice);
+ }
+ }
+
if (status == NO_ERROR) {
status = mOutput->setParameters(keyValuePair);
if (!mStandby && status == INVALID_OPERATION) {
@@ -1771,13 +2018,17 @@ uint32_t AudioFlinger::MixerThread::activeSleepTimeUs()
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs()
{
- return (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000;
+ return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
+}
+
+uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs()
+{
+ return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
}
// ----------------------------------------------------------------------------
-AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
- : PlaybackThread(audioFlinger, output, id),
- mLeftVolume (1.0), mRightVolume(1.0)
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+ : PlaybackThread(audioFlinger, output, id, device)
{
mType = PlaybackThread::DIRECT;
}
@@ -1787,6 +2038,102 @@ 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)
+{
+#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
+}
+
+void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp)
+{
+ // Do not apply volume on compressed audio
+ if (!AudioSystem::isLinearPCM(mFormat)) {
+ return;
+ }
+
+ // convert to signed 16 bit before volume calculation
+ if (mFormat == AudioSystem::PCM_8_BIT) {
+ size_t count = mFrameCount * mChannelCount;
+ uint8_t *src = (uint8_t *)mMixBuffer + count-1;
+ int16_t *dst = mMixBuffer + count-1;
+ while(count--) {
+ *dst-- = (int16_t)(*src--^0x80) << 8;
+ }
+ }
+
+ size_t frameCount = mFrameCount;
+ int16_t *out = mMixBuffer;
+ if (ramp) {
+ if (mChannelCount == 1) {
+ int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
+ int32_t vlInc = d / (int32_t)frameCount;
+ int32_t vl = ((int32_t)mLeftVolShort << 16);
+ do {
+ out[0] = clamp16(mul(out[0], vl >> 16) >> 12);
+ out++;
+ vl += vlInc;
+ } while (--frameCount);
+
+ } else {
+ int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16;
+ int32_t vlInc = d / (int32_t)frameCount;
+ d = ((int32_t)rightVol - (int32_t)mRightVolShort) << 16;
+ int32_t vrInc = d / (int32_t)frameCount;
+ int32_t vl = ((int32_t)mLeftVolShort << 16);
+ int32_t vr = ((int32_t)mRightVolShort << 16);
+ do {
+ out[0] = clamp16(mul(out[0], vl >> 16) >> 12);
+ out[1] = clamp16(mul(out[1], vr >> 16) >> 12);
+ out += 2;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+ }
+ } else {
+ if (mChannelCount == 1) {
+ do {
+ out[0] = clamp16(mul(out[0], leftVol) >> 12);
+ out++;
+ } while (--frameCount);
+ } else {
+ do {
+ out[0] = clamp16(mul(out[0], leftVol) >> 12);
+ out[1] = clamp16(mul(out[1], rightVol) >> 12);
+ out += 2;
+ } while (--frameCount);
+ }
+ }
+
+ // convert back to unsigned 8 bit after volume calculation
+ if (mFormat == AudioSystem::PCM_8_BIT) {
+ size_t count = mFrameCount * mChannelCount;
+ int16_t *src = mMixBuffer;
+ uint8_t *dst = (uint8_t *)mMixBuffer;
+ while(count--) {
+ *dst++ = (uint8_t)(((int32_t)*src++ + (1<<7)) >> 8)^0x80;
+ }
+ }
+
+ mLeftVolShort = leftVol;
+ mRightVolShort = rightVol;
+}
+
bool AudioFlinger::DirectOutputThread::threadLoop()
{
uint32_t mixerStatus = MIXER_IDLE;
@@ -1802,9 +2149,13 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
// hardware resources as soon as possible
nsecs_t standbyDelay = microseconds(activeSleepTime*2);
-
while (!exitPending())
{
+ bool rampVolume;
+ uint16_t leftVol;
+ uint16_t rightVol;
+ Vector< sp<EffectChain> > effectChains;
+
processConfigEvents();
mixerStatus = MIXER_IDLE;
@@ -1856,6 +2207,8 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
}
}
+ effectChains = mEffectChains;
+
// find out which tracks need to be processed
if (mActiveTracks.size() != 0) {
sp<Track> t = mActiveTracks[0].promote();
@@ -1871,6 +2224,19 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
{
//LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ mLeftVolFloat = mRightVolFloat = 0;
+ mLeftVolShort = mRightVolShort = 0;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ rampVolume = true;
+ }
+ } else if (cblk->server != 0) {
+ // If the track is stopped before the first frame was mixed,
+ // do not apply ramp
+ rampVolume = true;
+ }
// compute volume for this track
float left, right;
if (track->isMuted() || mMasterMute || track->isPausing() ||
@@ -1890,17 +2256,42 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
right = v_clamped/MAX_GAIN;
}
- if (left != mLeftVolume || right != mRightVolume) {
- mOutput->setVolume(left, right);
- left = mLeftVolume;
- right = mRightVolume;
- }
+ if (left != mLeftVolFloat || right != mRightVolFloat) {
+ mLeftVolFloat = left;
+ mRightVolFloat = right;
- if (track->mFillingUpStatus == Track::FS_FILLED) {
- track->mFillingUpStatus = Track::FS_ACTIVE;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
+ // If audio HAL implements volume control,
+ // force software volume to nominal value
+ if (mOutput->setVolume(left, right) == NO_ERROR) {
+ left = 1.0f;
+ right = 1.0f;
}
+
+ // Convert volumes from float to 8.24
+ uint32_t vl = (uint32_t)(left * (1 << 24));
+ uint32_t vr = (uint32_t)(right * (1 << 24));
+
+ // Delegate volume control to effect in track effect chain if needed
+ // only one effect chain can be present on DirectOutputThread, so if
+ // there is one, the track is connected to it
+ if (!effectChains.isEmpty()) {
+ // Do not ramp volume is volume is controlled by effect
+ if(effectChains[0]->setVolume_l(&vl, &vr)) {
+ rampVolume = false;
+ }
+ }
+
+ // Convert volumes from 8.24 to 4.12 format
+ uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ leftVol = (uint16_t)v_clamped;
+ v_clamped = (vr + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ rightVol = (uint16_t)v_clamped;
+ } else {
+ leftVol = mLeftVolShort;
+ rightVol = mRightVolShort;
+ rampVolume = false;
}
// reset retry count
@@ -1932,11 +2323,18 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
// remove all the tracks that need to be...
if (UNLIKELY(trackToRemove != 0)) {
mActiveTracks.remove(trackToRemove);
+ if (!effectChains.isEmpty()) {
+ LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
+ trackToRemove->sessionId());
+ effectChains[0]->stopTrack();
+ }
if (trackToRemove->isTerminated()) {
mTracks.remove(trackToRemove);
deleteTrackName_l(trackToRemove->mName);
}
}
+
+ lockEffectChains_l(effectChains);
}
if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
@@ -1944,7 +2342,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
size_t frameCount = mFrameCount;
curBuf = (int8_t *)mMixBuffer;
// output audio to hardware
- while(frameCount) {
+ while (frameCount) {
buffer.frameCount = frameCount;
activeTrack->getNextBuffer(&buffer);
if (UNLIKELY(buffer.raw == 0)) {
@@ -1972,10 +2370,18 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
+ if (mixerStatus == MIXER_TRACKS_READY) {
+ applyVolume(leftVol, rightVol, rampVolume);
+ }
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ unlockEffectChains(effectChains);
+
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
@@ -1985,6 +2391,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
mInWrite = false;
mStandby = false;
} else {
+ unlockEffectChains(effectChains);
usleep(sleepTime);
}
@@ -1993,6 +2400,10 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
// same lock.
trackToRemove.clear();
activeTrack.clear();
+
+ // Effect chains will be actually deleted here if they were removed from
+ // mEffectChains list during mixing or effects processing
+ effectChains.clear();
}
if (!mStandby) {
@@ -2073,17 +2484,29 @@ uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs()
{
uint32_t time;
if (AudioSystem::isLinearPCM(mFormat)) {
- time = (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000;
+ time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
+{
+ uint32_t time;
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
} else {
time = 10000;
}
return time;
}
+
// ----------------------------------------------------------------------------
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
- : MixerThread(audioFlinger, mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX)
+ : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX)
{
mType = PlaybackThread::DUPLICATING;
addOutputTrack(mainThread);
@@ -2099,7 +2522,6 @@ AudioFlinger::DuplicatingThread::~DuplicatingThread()
bool AudioFlinger::DuplicatingThread::threadLoop()
{
- int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
uint32_t mixerStatus = MIXER_IDLE;
nsecs_t standbyTime = systemTime();
@@ -2109,6 +2531,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
uint32_t activeSleepTime = activeSleepTimeUs();
uint32_t idleSleepTime = idleSleepTimeUs();
uint32_t sleepTime = idleSleepTime;
+ Vector< sp<EffectChain> > effectChains;
while (!exitPending())
{
@@ -2169,14 +2592,19 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
}
mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+
+ // prevent any changes in effect chain list and in each effect chain
+ // 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)) {
// mix buffers...
if (outputsReady(outputTracks)) {
- mAudioMixer->process(curBuf);
+ mAudioMixer->process();
} else {
- memset(curBuf, 0, mixBufferSize);
+ memset(mMixBuffer, 0, mixBufferSize);
}
sleepTime = 0;
writeFrames = mFrameCount;
@@ -2193,6 +2621,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
if (outputTracks[i]->isActive()) {
sleepTime = 0;
writeFrames = 0;
+ memset(mMixBuffer, 0, mixBufferSize);
break;
}
}
@@ -2200,17 +2629,25 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// 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);
+
standbyTime = systemTime() + kStandbyTimeInNsecs;
for (size_t i = 0; i < outputTracks.size(); i++) {
- outputTracks[i]->write(curBuf, writeFrames);
+ outputTracks[i]->write(mMixBuffer, writeFrames);
}
mStandby = false;
mBytesWritten += mixBufferSize;
} else {
+ // enable changes in effect chain
+ unlockEffectChains(effectChains);
usleep(sleepTime);
}
@@ -2219,6 +2656,10 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
// same lock.
tracksToRemove.clear();
outputTracks.clear();
+
+ // Effect chains will be actually deleted here if they were removed from
+ // mEffectChains list during mixing or effects processing
+ effectChains.clear();
}
return false;
@@ -2303,7 +2744,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
int channelCount,
int frameCount,
uint32_t flags,
- const sp<IMemory>& sharedBuffer)
+ const sp<IMemory>& sharedBuffer,
+ int sessionId)
: RefBase(),
mThread(thread),
mClient(client),
@@ -2312,7 +2754,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
mState(IDLE),
mClientTid(-1),
mFormat(format),
- mFlags(flags & ~SYSTEM_FLAGS_MASK)
+ mFlags(flags & ~SYSTEM_FLAGS_MASK),
+ mSessionId(sessionId)
{
LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
@@ -2332,13 +2775,13 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
// clear all buffers
mCblk->frameCount = frameCount;
mCblk->sampleRate = sampleRate;
- mCblk->channels = (uint8_t)channelCount;
+ mCblk->channelCount = (uint8_t)channelCount;
if (sharedBuffer == 0) {
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
// Force underrun condition to avoid false underrun callback until first data is
// written to buffer
- mCblk->flowControlFlag = 1;
+ mCblk->flags = CBLK_UNDERRUN_ON;
} else {
mBuffer = sharedBuffer->pointer();
}
@@ -2356,12 +2799,12 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
// clear all buffers
mCblk->frameCount = frameCount;
mCblk->sampleRate = sampleRate;
- mCblk->channels = (uint8_t)channelCount;
+ mCblk->channelCount = (uint8_t)channelCount;
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
// Force underrun condition to avoid false underrun callback until first data is
// written to buffer
- mCblk->flowControlFlag = 1;
+ mCblk->flags = CBLK_UNDERRUN_ON;
mBufferEnd = (uint8_t *)mBuffer + bufferSize;
}
}
@@ -2423,7 +2866,7 @@ int AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
}
int AudioFlinger::ThreadBase::TrackBase::channelCount() const {
- return (int)mCblk->channels;
+ return (int)mCblk->channelCount;
}
void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
@@ -2435,9 +2878,9 @@ void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t f
if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd ||
((unsigned long)bufferStart & (unsigned long)(cblk->frameSize - 1))) {
LOGE("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \
- server %d, serverBase %d, user %d, userBase %d, channels %d",
+ server %d, serverBase %d, user %d, userBase %d, channelCount %d",
bufferStart, bufferEnd, mBuffer, mBufferEnd,
- cblk->server, cblk->serverBase, cblk->user, cblk->userBase, cblk->channels);
+ cblk->server, cblk->serverBase, cblk->user, cblk->userBase, cblk->channelCount);
return 0;
}
@@ -2455,15 +2898,17 @@ AudioFlinger::PlaybackThread::Track::Track(
int format,
int channelCount,
int frameCount,
- const sp<IMemory>& sharedBuffer)
- : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer),
- mMute(false), mSharedBuffer(sharedBuffer), mName(-1)
+ const sp<IMemory>& sharedBuffer,
+ int sessionId)
+ : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer, sessionId),
+ mMute(false), mSharedBuffer(sharedBuffer), mName(-1), mMainBuffer(NULL), mAuxBuffer(NULL), mAuxEffectId(0)
{
if (mCblk != NULL) {
sp<ThreadBase> baseThread = thread.promote();
if (baseThread != 0) {
PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get();
mName = playbackThread->getTrackName_l();
+ mMainBuffer = playbackThread->mixBuffer();
}
LOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
if (mName < 0) {
@@ -2504,7 +2949,9 @@ void AudioFlinger::PlaybackThread::Track::destroy()
if (thread != 0) {
if (!isOutputTrack()) {
if (mState == ACTIVE || mState == RESUMING) {
- AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ AudioSystem::stopOutput(thread->id(),
+ (AudioSystem::stream_type)mStreamType,
+ mSessionId);
}
AudioSystem::releaseOutput(thread->id());
}
@@ -2517,12 +2964,13 @@ void AudioFlinger::PlaybackThread::Track::destroy()
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %5d %5d %3u %3u %3u %04u %1d %1d %1d %5u %5u %5u %08x %08x\n",
+ snprintf(buffer, size, " %05d %05d %03u %03u %03u %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(),
mStreamType,
mFormat,
- mCblk->channels,
+ mCblk->channelCount,
+ mSessionId,
mFrameCount,
mState,
mMute,
@@ -2531,7 +2979,9 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
mCblk->volume[0],
mCblk->volume[1],
mCblk->server,
- mCblk->user);
+ mCblk->user,
+ (int)mMainBuffer,
+ (int)mAuxBuffer);
}
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
@@ -2579,9 +3029,9 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const {
if (mFillingUpStatus != FS_FILLING) return true;
if (mCblk->framesReady() >= mCblk->frameCount ||
- mCblk->forceReady) {
+ (mCblk->flags & CBLK_FORCEREADY_MSK)) {
mFillingUpStatus = FS_FILLED;
- mCblk->forceReady = 0;
+ mCblk->flags &= ~CBLK_FORCEREADY_MSK;
return true;
}
return false;
@@ -2590,7 +3040,8 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const {
status_t AudioFlinger::PlaybackThread::Track::start()
{
status_t status = NO_ERROR;
- LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+ LOGV("start(%d), calling thread %d session %d",
+ mName, IPCThreadState::self()->getCallingPid(), mSessionId);
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
@@ -2607,7 +3058,9 @@ status_t AudioFlinger::PlaybackThread::Track::start()
if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
thread->mLock.unlock();
- status = AudioSystem::startOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ status = AudioSystem::startOutput(thread->id(),
+ (AudioSystem::stream_type)mStreamType,
+ mSessionId);
thread->mLock.lock();
}
if (status == NO_ERROR) {
@@ -2640,7 +3093,9 @@ void AudioFlinger::PlaybackThread::Track::stop()
}
if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
thread->mLock.unlock();
- AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ AudioSystem::stopOutput(thread->id(),
+ (AudioSystem::stream_type)mStreamType,
+ mSessionId);
thread->mLock.lock();
}
}
@@ -2657,7 +3112,9 @@ void AudioFlinger::PlaybackThread::Track::pause()
LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
if (!isOutputTrack()) {
thread->mLock.unlock();
- AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ AudioSystem::stopOutput(thread->id(),
+ (AudioSystem::stream_type)mStreamType,
+ mSessionId);
thread->mLock.lock();
}
}
@@ -2696,8 +3153,8 @@ void AudioFlinger::PlaybackThread::Track::reset()
TrackBase::reset();
// Force underrun condition to avoid false underrun callback until first data is
// written to buffer
- mCblk->flowControlFlag = 1;
- mCblk->forceReady = 0;
+ mCblk->flags |= CBLK_UNDERRUN_ON;
+ mCblk->flags &= ~CBLK_FORCEREADY_MSK;
mFillingUpStatus = FS_FILLING;
mResetDone = true;
}
@@ -2714,6 +3171,23 @@ void AudioFlinger::PlaybackThread::Track::setVolume(float left, float right)
mVolume[1] = right;
}
+status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
+{
+ status_t status = DEAD_OBJECT;
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ status = playbackThread->attachAuxEffect(this, EffectId);
+ }
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::Track::setAuxBuffer(int EffectId, int32_t *buffer)
+{
+ mAuxEffectId = EffectId;
+ mAuxBuffer = buffer;
+}
+
// ----------------------------------------------------------------------------
// RecordTrack constructor must be called with AudioFlinger::mLock held
@@ -2724,9 +3198,10 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
int format,
int channelCount,
int frameCount,
- uint32_t flags)
+ uint32_t flags,
+ int sessionId)
: TrackBase(thread, client, sampleRate, format,
- channelCount, frameCount, flags, 0),
+ channelCount, frameCount, flags, 0, sessionId),
mOverflow(false)
{
if (mCblk != NULL) {
@@ -2808,16 +3283,17 @@ void AudioFlinger::RecordThread::RecordTrack::stop()
TrackBase::reset();
// Force overerrun condition to avoid false overrun callback until first data is
// read from buffer
- mCblk->flowControlFlag = 1;
+ mCblk->flags |= CBLK_UNDERRUN_ON;
}
}
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %05d %03u %03u %04u %01d %05u %08x %08x\n",
+ snprintf(buffer, size, " %05d %03u %03u %05d %04u %01d %05u %08x %08x\n",
(mClient == NULL) ? getpid() : mClient->pid(),
mFormat,
- mCblk->channels,
+ mCblk->channelCount,
+ mSessionId,
mFrameCount,
mState,
mCblk->sampleRate,
@@ -2835,19 +3311,19 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
int format,
int channelCount,
int frameCount)
- : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL),
+ : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL, 0),
mActive(false), mSourceThread(sourceThread)
{
PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get();
if (mCblk != NULL) {
- mCblk->out = 1;
+ 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);
- LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p",
- mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd);
+ LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channelCount %d mBufferEnd %p",
+ mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channelCount, mBufferEnd);
} else {
LOGW("Error creating output track on thread %p", playbackThread);
}
@@ -2882,7 +3358,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
{
Buffer *pInBuffer;
Buffer inBuffer;
- uint32_t channels = mCblk->channels;
+ uint32_t channelCount = mCblk->channelCount;
bool outputBufferFull = false;
inBuffer.frameCount = frames;
inBuffer.i16 = data;
@@ -2898,10 +3374,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
if (mBufferQueue.size() < kMaxOverFlowBuffers) {
uint32_t startFrames = (mCblk->frameCount - frames);
pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[startFrames * channels];
+ pInBuffer->mBuffer = new int16_t[startFrames * channelCount];
pInBuffer->frameCount = startFrames;
pInBuffer->i16 = pInBuffer->mBuffer;
- memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t));
+ memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t));
mBufferQueue.add(pInBuffer);
} else {
LOGW ("OutputTrack::write() %p no more buffers in queue", this);
@@ -2939,12 +3415,12 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
}
uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount;
- memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t));
+ memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
mCblk->stepUser(outFrames);
pInBuffer->frameCount -= outFrames;
- pInBuffer->i16 += outFrames * channels;
+ pInBuffer->i16 += outFrames * channelCount;
mOutBuffer.frameCount -= outFrames;
- mOutBuffer.i16 += outFrames * channels;
+ mOutBuffer.i16 += outFrames * channelCount;
if (pInBuffer->frameCount == 0) {
if (mBufferQueue.size()) {
@@ -2964,10 +3440,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
if (thread != 0 && !thread->standby()) {
if (mBufferQueue.size() < kMaxOverFlowBuffers) {
pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels];
+ pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channelCount];
pInBuffer->frameCount = inBuffer.frameCount;
pInBuffer->i16 = pInBuffer->mBuffer;
- memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t));
+ memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channelCount * sizeof(int16_t));
mBufferQueue.add(pInBuffer);
LOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size());
} else {
@@ -2983,10 +3459,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
if (mCblk->user < mCblk->frameCount) {
frames = mCblk->frameCount - mCblk->user;
pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[frames * channels];
+ pInBuffer->mBuffer = new int16_t[frames * channelCount];
pInBuffer->frameCount = frames;
pInBuffer->i16 = pInBuffer->mBuffer;
- memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t));
+ memset(pInBuffer->raw, 0, frames * channelCount * sizeof(int16_t));
mBufferQueue.add(pInBuffer);
} else if (mActive) {
stop();
@@ -3086,6 +3562,28 @@ 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)
+{
+}
+
+AudioFlinger::NotificationClient::~NotificationClient()
+{
+ mClient.clear();
+}
+
+void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
+{
+ sp<NotificationClient> keep(this);
+ {
+ mAudioFlinger->removeNotificationClient(mPid);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track)
: BnAudioTrack(),
mTrack(track)
@@ -3128,6 +3626,11 @@ sp<IMemory> AudioFlinger::TrackHandle::getCblk() const {
return mTrack->getCblk();
}
+status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId)
+{
+ return mTrack->attachAuxEffect(EffectId);
+}
+
status_t AudioFlinger::TrackHandle::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -3144,6 +3647,7 @@ sp<IAudioRecord> AudioFlinger::openRecord(
int channelCount,
int frameCount,
uint32_t flags,
+ int *sessionId,
status_t *status)
{
sp<RecordThread::RecordTrack> recordTrack;
@@ -3153,6 +3657,7 @@ sp<IAudioRecord> AudioFlinger::openRecord(
status_t lStatus;
RecordThread *thread;
size_t inFrameCount;
+ int lSessionId;
// check calling permissions
if (!recordingAllowed()) {
@@ -3177,9 +3682,18 @@ sp<IAudioRecord> AudioFlinger::openRecord(
mClients.add(pid, client);
}
+ // If no audio session id is provided, create one here
+ if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+ lSessionId = *sessionId;
+ } else {
+ lSessionId = nextUniqueId();
+ if (sessionId != NULL) {
+ *sessionId = lSessionId;
+ }
+ }
// create new record track. The record track uses one track in mHardwareMixerThread by convention.
recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate,
- format, channelCount, frameCount, flags);
+ format, channelCount, frameCount, flags, lSessionId);
}
if (recordTrack->getCblk() == NULL) {
// remove local strong reference to Client before deleting the RecordTrack so that the Client
@@ -3242,7 +3756,6 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, A
mReqChannelCount = AudioSystem::popCount(channels);
mReqSampleRate = sampleRate;
readInputParameters();
- sendConfigEvent(AudioSystem::INPUT_OPENED);
}
@@ -3339,7 +3852,7 @@ bool AudioFlinger::RecordThread::threadLoop()
framesIn = framesOut;
mRsmpInIndex += framesIn;
framesOut -= framesIn;
- if (mChannelCount == mReqChannelCount ||
+ if ((int)mChannelCount == mReqChannelCount ||
mFormat != AudioSystem::PCM_16_BIT) {
memcpy(dst, src, framesIn * mFrameSize);
} else {
@@ -3360,7 +3873,7 @@ bool AudioFlinger::RecordThread::threadLoop()
}
if (framesOut && mFrameCount == mRsmpInIndex) {
if (framesOut == mFrameCount &&
- (mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) {
+ ((int)mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) {
mBytesRead = mInput->read(buffer.raw, mInputBytes);
framesOut = 0;
} else {
@@ -3518,7 +4031,7 @@ status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
if (mActiveTrack != 0) {
result.append("Active Track:\n");
- result.append(" Clien Fmt Chn Buf S SRate Serv User\n");
+ result.append(" Clien Fmt Chn Session Buf S SRate Serv User\n");
mActiveTrack->dump(buffer, SIZE);
result.append(buffer);
@@ -3657,14 +4170,14 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
return mInput->getParameters(keys);
}
-void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) {
+void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
AudioSystem::OutputDescriptor desc;
void *param2 = 0;
switch (event) {
case AudioSystem::INPUT_OPENED:
case AudioSystem::INPUT_CONFIG_CHANGED:
- desc.channels = mChannelCount;
+ desc.channels = mChannels;
desc.samplingRate = mSampleRate;
desc.format = mFormat;
desc.frameCount = mFrameCount;
@@ -3676,7 +4189,6 @@ void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) {
default:
break;
}
- Mutex::Autolock _l(mAudioFlinger->mLock);
mAudioFlinger->audioConfigChanged_l(event, mId, param2);
}
@@ -3688,9 +4200,10 @@ void AudioFlinger::RecordThread::readInputParameters()
mResampler = 0;
mSampleRate = mInput->sampleRate();
- mChannelCount = AudioSystem::popCount(mInput->channels());
+ mChannels = mInput->channels();
+ mChannelCount = (uint16_t)AudioSystem::popCount(mChannels);
mFormat = mInput->format();
- mFrameSize = mInput->frameSize();
+ mFrameSize = (uint16_t)mInput->frameSize();
mInputBytes = mInput->bufferSize();
mFrameCount = mInputBytes / mFrameSize;
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
@@ -3767,14 +4280,15 @@ int AudioFlinger::openOutput(uint32_t *pDevices,
mHardwareStatus = AUDIO_HW_IDLE;
if (output != 0) {
+ int id = nextUniqueId();
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
(format != AudioSystem::PCM_16_BIT) ||
(channels != AudioSystem::CHANNEL_OUT_STEREO)) {
- thread = new DirectOutputThread(this, output, ++mNextThreadId);
- LOGV("openOutput() created direct output: ID %d thread %p", mNextThreadId, thread);
+ thread = new DirectOutputThread(this, output, id, *pDevices);
+ LOGV("openOutput() created direct output: ID %d thread %p", id, thread);
} else {
- thread = new MixerThread(this, output, ++mNextThreadId);
- LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread);
+ thread = new MixerThread(this, output, id, *pDevices);
+ LOGV("openOutput() created mixer output: ID %d thread %p", id, thread);
#ifdef LVMX
unsigned bitsPerSample =
@@ -3788,14 +4302,16 @@ int AudioFlinger::openOutput(uint32_t *pDevices,
#endif
}
- mPlaybackThreads.add(mNextThreadId, thread);
+ mPlaybackThreads.add(id, thread);
if (pSamplingRate) *pSamplingRate = samplingRate;
if (pFormat) *pFormat = format;
if (pChannels) *pChannels = channels;
if (pLatencyMs) *pLatencyMs = thread->latency();
- return mNextThreadId;
+ // notify client processes of the new output creation
+ thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
+ return id;
}
return 0;
@@ -3812,11 +4328,13 @@ int AudioFlinger::openDuplicateOutput(int output1, int output2)
return 0;
}
-
- DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId);
+ int id = nextUniqueId();
+ DuplicatingThread *thread = new DuplicatingThread(this, thread1, id);
thread->addOutputTrack(thread2);
- mPlaybackThreads.add(mNextThreadId, thread);
- return mNextThreadId;
+ mPlaybackThreads.add(id, thread);
+ // notify client processes of the new output creation
+ thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
+ return id;
}
status_t AudioFlinger::closeOutput(int output)
@@ -3935,17 +4453,20 @@ int AudioFlinger::openInput(uint32_t *pDevices,
}
if (input != 0) {
+ int id = nextUniqueId();
// Start record thread
- thread = new RecordThread(this, input, reqSamplingRate, reqChannels, ++mNextThreadId);
- mRecordThreads.add(mNextThreadId, thread);
- LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread);
+ thread = new RecordThread(this, input, reqSamplingRate, reqChannels, id);
+ mRecordThreads.add(id, thread);
+ LOGV("openInput() created record thread: ID %d thread %p", id, thread);
if (pSamplingRate) *pSamplingRate = reqSamplingRate;
if (pFormat) *pFormat = format;
if (pChannels) *pChannels = reqChannels;
input->standby();
- return mNextThreadId;
+ // notify client processes of the new input creation
+ thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
+ return id;
}
return 0;
@@ -3985,26 +4506,26 @@ status_t AudioFlinger::setStreamOutput(uint32_t stream, int output)
}
LOGV("setStreamOutput() stream %d to output %d", stream, output);
+ audioConfigChanged_l(AudioSystem::STREAM_CONFIG_CHANGED, output, &stream);
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
if (thread != dstThread &&
thread->type() != PlaybackThread::DIRECT) {
MixerThread *srcThread = (MixerThread *)thread;
- SortedVector < sp<MixerThread::Track> > tracks;
- SortedVector < wp<MixerThread::Track> > activeTracks;
- srcThread->getTracks(tracks, activeTracks, stream);
- if (tracks.size()) {
- dstThread->putTracks(tracks, activeTracks);
- }
+ srcThread->invalidateTracks(stream);
}
}
- dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream);
-
return NO_ERROR;
}
+
+int AudioFlinger::newAudioSessionId()
+{
+ return nextUniqueId();
+}
+
// checkPlaybackThread_l() must be called with AudioFlinger::mLock held
AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
{
@@ -4037,19 +4558,1833 @@ AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(int input) const
return thread;
}
+int AudioFlinger::nextUniqueId()
+{
+ return android_atomic_inc(&mNextUniqueId);
+}
+
+// ----------------------------------------------------------------------------
+// Effect management
// ----------------------------------------------------------------------------
-status_t AudioFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+
+status_t AudioFlinger::loadEffectLibrary(const char *libPath, int *handle)
{
- return BnAudioFlinger::onTransact(code, data, reply, flags);
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ // only allow libraries loaded from /system/lib/soundfx for now
+ if (strncmp(gEffectLibPath, libPath, strlen(gEffectLibPath)) != 0) {
+ return PERMISSION_DENIED;
+ }
+
+ Mutex::Autolock _l(mLock);
+ return EffectLoadLibrary(libPath, handle);
+}
+
+status_t AudioFlinger::unloadEffectLibrary(int handle)
+{
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ Mutex::Autolock _l(mLock);
+ return EffectUnloadLibrary(handle);
+}
+
+status_t AudioFlinger::queryNumberEffects(uint32_t *numEffects)
+{
+ Mutex::Autolock _l(mLock);
+ return EffectQueryNumberEffects(numEffects);
+}
+
+status_t AudioFlinger::queryEffect(uint32_t index, effect_descriptor_t *descriptor)
+{
+ Mutex::Autolock _l(mLock);
+ return EffectQueryEffect(index, descriptor);
+}
+
+status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor)
+{
+ Mutex::Autolock _l(mLock);
+ return EffectGetDescriptor(pUuid, descriptor);
+}
+
+
+// this UUID must match the one defined in media/libeffects/EffectVisualizer.cpp
+static const effect_uuid_t VISUALIZATION_UUID_ =
+ {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
+sp<IEffect> AudioFlinger::createEffect(pid_t pid,
+ effect_descriptor_t *pDesc,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ int output,
+ int sessionId,
+ status_t *status,
+ int *id,
+ int *enabled)
+{
+ status_t lStatus = NO_ERROR;
+ sp<EffectHandle> handle;
+ effect_interface_t itfe;
+ effect_descriptor_t desc;
+ sp<Client> client;
+ wp<Client> wclient;
+
+ LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d",
+ pid, effectClient.get(), priority, sessionId, output);
+
+ if (pDesc == NULL) {
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ {
+ Mutex::Autolock _l(mLock);
+
+ // check recording permission for visualizer
+ if (memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
+ memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) {
+ if (!recordingAllowed()) {
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+ }
+
+ if (!EffectIsNullUuid(&pDesc->uuid)) {
+ // if uuid is specified, request effect descriptor
+ lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
+ if (lStatus < 0) {
+ LOGW("createEffect() error %d from EffectGetDescriptor", lStatus);
+ goto Exit;
+ }
+ } else {
+ // if uuid is not specified, look for an available implementation
+ // of the required type in effect factory
+ if (EffectIsNullUuid(&pDesc->type)) {
+ LOGW("createEffect() no effect type");
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ uint32_t numEffects = 0;
+ effect_descriptor_t d;
+ bool found = false;
+
+ lStatus = EffectQueryNumberEffects(&numEffects);
+ if (lStatus < 0) {
+ LOGW("createEffect() error %d from EffectQueryNumberEffects", lStatus);
+ goto Exit;
+ }
+ for (uint32_t i = 0; i < numEffects; i++) {
+ lStatus = EffectQueryEffect(i, &desc);
+ if (lStatus < 0) {
+ LOGW("createEffect() error %d from EffectQueryEffect", lStatus);
+ continue;
+ }
+ if (memcmp(&desc.type, &pDesc->type, sizeof(effect_uuid_t)) == 0) {
+ // If matching type found save effect descriptor. If the session is
+ // 0 and the effect is not auxiliary, continue enumeration in case
+ // an auxiliary version of this effect type is available
+ found = true;
+ memcpy(&d, &desc, sizeof(effect_descriptor_t));
+ if (sessionId != AudioSystem::SESSION_OUTPUT_MIX ||
+ (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ break;
+ }
+ }
+ }
+ if (!found) {
+ lStatus = BAD_VALUE;
+ LOGW("createEffect() effect not found");
+ goto Exit;
+ }
+ // For same effect type, chose auxiliary version over insert version if
+ // connect to output mix (Compliance to OpenSL ES)
+ if (sessionId == AudioSystem::SESSION_OUTPUT_MIX &&
+ (d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) {
+ memcpy(&desc, &d, sizeof(effect_descriptor_t));
+ }
+ }
+
+ // Do not allow auxiliary effects on a session different from 0 (output mix)
+ if (sessionId != AudioSystem::SESSION_OUTPUT_MIX &&
+ (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+
+ // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
+ // that can only be created by audio policy manager (running in same process)
+ if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE &&
+ getpid() != IPCThreadState::self()->getCallingPid()) {
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+
+ // return effect descriptor
+ memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
+
+ // If output is not specified try to find a matching audio session ID in one of the
+ // output threads.
+ // TODO: allow attachment of effect to inputs
+ if (output == 0) {
+ if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
+ // output must be specified by AudioPolicyManager when using session
+ // AudioSystem::SESSION_OUTPUT_STAGE
+ lStatus = BAD_VALUE;
+ goto Exit;
+ } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+ output = AudioSystem::getOutputForEffect(&desc);
+ LOGV("createEffect() got output %d for effect %s", output, desc.name);
+ } else {
+ // look for the thread where the specified audio session is present
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
+ output = mPlaybackThreads.keyAt(i);
+ break;
+ }
+ }
+ // If no output thread contains the requested session ID, default to
+ // first output. The effect chain will be moved to the correct output
+ // thread when a track with the same session ID is created
+ if (output == 0 && mPlaybackThreads.size()) {
+ output = mPlaybackThreads.keyAt(0);
+ }
+ }
+ }
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGE("createEffect() unknown output thread");
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ wclient = mClients.valueFor(pid);
+
+ if (wclient != NULL) {
+ client = wclient.promote();
+ } else {
+ client = new Client(this, pid);
+ mClients.add(pid, client);
+ }
+
+ // create effect on selected output trhead
+ handle = thread->createEffect_l(client, effectClient, priority, sessionId,
+ &desc, enabled, &lStatus);
+ if (handle != 0 && id != NULL) {
+ *id = handle->id();
+ }
+ }
+
+Exit:
+ if(status) {
+ *status = lStatus;
+ }
+ return handle;
+}
+
+status_t AudioFlinger::moveEffects(int session, int srcOutput, int dstOutput)
+{
+ LOGV("moveEffects() session %d, srcOutput %d, dstOutput %d",
+ session, srcOutput, dstOutput);
+ Mutex::Autolock _l(mLock);
+ if (srcOutput == dstOutput) {
+ LOGW("moveEffects() same dst and src outputs %d", dstOutput);
+ return NO_ERROR;
+ }
+ PlaybackThread *srcThread = checkPlaybackThread_l(srcOutput);
+ if (srcThread == NULL) {
+ LOGW("moveEffects() bad srcOutput %d", srcOutput);
+ return BAD_VALUE;
+ }
+ PlaybackThread *dstThread = checkPlaybackThread_l(dstOutput);
+ if (dstThread == NULL) {
+ LOGW("moveEffects() bad dstOutput %d", dstOutput);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _dl(dstThread->mLock);
+ Mutex::Autolock _sl(srcThread->mLock);
+ moveEffectChain_l(session, srcThread, dstThread, false);
+
+ return NO_ERROR;
+}
+
+// moveEffectChain_l mustbe called with both srcThread and dstThread mLocks held
+status_t AudioFlinger::moveEffectChain_l(int session,
+ AudioFlinger::PlaybackThread *srcThread,
+ AudioFlinger::PlaybackThread *dstThread,
+ bool reRegister)
+{
+ LOGV("moveEffectChain_l() session %d from thread %p to thread %p",
+ session, srcThread, dstThread);
+
+ sp<EffectChain> chain = srcThread->getEffectChain_l(session);
+ if (chain == 0) {
+ LOGW("moveEffectChain_l() effect chain for session %d not on source thread %p",
+ session, srcThread);
+ return INVALID_OPERATION;
+ }
+
+ // remove chain first. This is useful only if reconfiguring effect chain on same output thread,
+ // so that a new chain is created with correct parameters when first effect is added. This is
+ // otherwise unecessary as removeEffect_l() will remove the chain when last effect is
+ // removed.
+ srcThread->removeEffectChain_l(chain);
+
+ // transfer all effects one by one so that new effect chain is created on new thread with
+ // correct buffer sizes and audio parameters and effect engines reconfigured accordingly
+ int dstOutput = dstThread->id();
+ sp<EffectChain> dstChain;
+ uint32_t strategy;
+ sp<EffectModule> effect = chain->getEffectFromId_l(0);
+ while (effect != 0) {
+ srcThread->removeEffect_l(effect);
+ dstThread->addEffect_l(effect);
+ // if the move request is not received from audio policy manager, the effect must be
+ // re-registered with the new strategy and output
+ if (dstChain == 0) {
+ dstChain = effect->chain().promote();
+ if (dstChain == 0) {
+ LOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
+ srcThread->addEffect_l(effect);
+ return NO_INIT;
+ }
+ strategy = dstChain->strategy();
+ }
+ if (reRegister) {
+ AudioSystem::unregisterEffect(effect->id());
+ AudioSystem::registerEffect(&effect->desc(),
+ dstOutput,
+ strategy,
+ session,
+ effect->id());
+ }
+ effect = chain->getEffectFromId_l(0);
+ }
+
+ return NO_ERROR;
}
+// PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l(
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ int sessionId,
+ effect_descriptor_t *desc,
+ int *enabled,
+ status_t *status
+ )
+{
+ sp<EffectModule> effect;
+ sp<EffectHandle> handle;
+ status_t lStatus;
+ sp<Track> track;
+ sp<EffectChain> chain;
+ bool chainCreated = false;
+ bool effectCreated = false;
+ bool effectRegistered = false;
+
+ if (mOutput == 0) {
+ LOGW("createEffect_l() Audio driver not initialized.");
+ lStatus = NO_INIT;
+ goto Exit;
+ }
+
+ // Do not allow auxiliary effect on session other than 0
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY &&
+ sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+ LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
+ desc->name, sessionId);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ // Do not allow effects with session ID 0 on direct output or duplicating threads
+ // TODO: add rule for hw accelerated effects on direct outputs with non PCM format
+ if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && mType != MIXER) {
+ LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
+ desc->name, sessionId);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ LOGV("createEffect_l() thread %p effect %s on session %d", this, desc->name, sessionId);
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+
+ // check for existing effect chain with the requested audio session
+ chain = getEffectChain_l(sessionId);
+ if (chain == 0) {
+ // create a new chain for this session
+ LOGV("createEffect_l() new effect chain for session %d", sessionId);
+ chain = new EffectChain(this, sessionId);
+ addEffectChain_l(chain);
+ chain->setStrategy(getStrategyForSession_l(sessionId));
+ chainCreated = true;
+ } else {
+ effect = chain->getEffectFromDesc_l(desc);
+ }
+
+ LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
+
+ if (effect == 0) {
+ int id = mAudioFlinger->nextUniqueId();
+ // Check CPU and memory usage
+ lStatus = AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+ effectRegistered = true;
+ // create a new effect module if none present in the chain
+ effect = new EffectModule(this, chain, desc, id, sessionId);
+ lStatus = effect->status();
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+ lStatus = chain->addEffect_l(effect);
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+ effectCreated = true;
+
+ effect->setDevice(mDevice);
+ effect->setMode(mAudioFlinger->getMode());
+ }
+ // create effect handle and connect it to effect module
+ handle = new EffectHandle(effect, client, effectClient, priority);
+ lStatus = effect->addHandle(handle);
+ if (enabled) {
+ *enabled = (int)effect->isEnabled();
+ }
+ }
+
+Exit:
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+ Mutex::Autolock _l(mLock);
+ if (effectCreated) {
+ chain->removeEffect_l(effect);
+ }
+ if (effectRegistered) {
+ AudioSystem::unregisterEffect(effect->id());
+ }
+ if (chainCreated) {
+ removeEffectChain_l(chain);
+ }
+ handle.clear();
+ }
+
+ if(status) {
+ *status = lStatus;
+ }
+ return handle;
+}
+
+// PlaybackThread::addEffect_l() must be called with AudioFlinger::mLock and
+// PlaybackThread::mLock held
+status_t AudioFlinger::PlaybackThread::addEffect_l(const sp<EffectModule>& effect)
+{
+ // check for existing effect chain with the requested audio session
+ int sessionId = effect->sessionId();
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ bool chainCreated = false;
+
+ if (chain == 0) {
+ // create a new chain for this session
+ LOGV("addEffect_l() new effect chain for session %d", sessionId);
+ chain = new EffectChain(this, sessionId);
+ addEffectChain_l(chain);
+ chain->setStrategy(getStrategyForSession_l(sessionId));
+ chainCreated = true;
+ }
+ LOGV("addEffect_l() %p chain %p effect %p", this, chain.get(), effect.get());
+
+ if (chain->getEffectFromId_l(effect->id()) != 0) {
+ LOGW("addEffect_l() %p effect %s already present in chain %p",
+ this, effect->desc().name, chain.get());
+ return BAD_VALUE;
+ }
+
+ status_t status = chain->addEffect_l(effect);
+ if (status != NO_ERROR) {
+ if (chainCreated) {
+ removeEffectChain_l(chain);
+ }
+ return status;
+ }
+
+ effect->setDevice(mDevice);
+ effect->setMode(mAudioFlinger->getMode());
+ return NO_ERROR;
+}
+
+void AudioFlinger::PlaybackThread::removeEffect_l(const sp<EffectModule>& effect) {
+
+ LOGV("removeEffect_l() %p effect %p", this, effect.get());
+ effect_descriptor_t desc = effect->desc();
+ if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ detachAuxEffect_l(effect->id());
+ }
+
+ sp<EffectChain> chain = effect->chain().promote();
+ if (chain != 0) {
+ // remove effect chain if removing last effect
+ if (chain->removeEffect_l(effect) == 0) {
+ removeEffectChain_l(chain);
+ }
+ } else {
+ LOGW("removeEffect_l() %p cannot promote chain for effect %p", this, effect.get());
+ }
+}
+
+void AudioFlinger::PlaybackThread::disconnectEffect(const sp<EffectModule>& effect,
+ const wp<EffectHandle>& handle) {
+ Mutex::Autolock _l(mLock);
+ LOGV("disconnectEffect() %p effect %p", this, effect.get());
+ // delete the effect module if removing last handle on it
+ if (effect->removeHandle(handle) == 0) {
+ removeEffect_l(effect);
+ AudioSystem::unregisterEffect(effect->id());
+ }
+}
+
+status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
+{
+ int session = chain->sessionId();
+ int16_t *buffer = mMixBuffer;
+ bool ownsBuffer = false;
+
+ LOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
+ if (session > 0) {
+ // Only one effect chain can be present in direct output thread and it uses
+ // the mix buffer as input
+ if (mType != DIRECT) {
+ size_t numSamples = mFrameCount * mChannelCount;
+ buffer = new int16_t[numSamples];
+ memset(buffer, 0, numSamples * sizeof(int16_t));
+ LOGV("addEffectChain_l() creating new input buffer %p session %d", buffer, session);
+ ownsBuffer = true;
+ }
+
+ // Attach all tracks with same session ID to this chain.
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (session == track->sessionId()) {
+ LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
+ track->setMainBuffer(buffer);
+ }
+ }
+
+ // indicate all active tracks in the chain
+ for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
+ sp<Track> track = mActiveTracks[i].promote();
+ if (track == 0) continue;
+ if (session == track->sessionId()) {
+ LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
+ chain->startTrack();
+ }
+ }
+ }
+
+ chain->setInBuffer(buffer, ownsBuffer);
+ chain->setOutBuffer(mMixBuffer);
+ // Effect chain for session AudioSystem::SESSION_OUTPUT_STAGE is inserted at end of effect
+ // chains list in order to be processed last as it contains output stage effects
+ // Effect chain for session AudioSystem::SESSION_OUTPUT_MIX is inserted before
+ // session AudioSystem::SESSION_OUTPUT_STAGE to be processed
+ // after track specific effects and before output stage
+ // It is therefore mandatory that AudioSystem::SESSION_OUTPUT_MIX == 0 and
+ // that AudioSystem::SESSION_OUTPUT_STAGE < AudioSystem::SESSION_OUTPUT_MIX
+ // Effect chain for other sessions are inserted at beginning of effect
+ // chains list to be processed before output mix effects. Relative order between other
+ // sessions is not important
+ size_t size = mEffectChains.size();
+ size_t i = 0;
+ for (i = 0; i < size; i++) {
+ if (mEffectChains[i]->sessionId() < session) break;
+ }
+ mEffectChains.insertAt(chain, i);
+
+ return NO_ERROR;
+}
+
+size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& chain)
+{
+ int session = chain->sessionId();
+
+ LOGV("removeEffectChain_l() %p from thread %p for session %d", chain.get(), this, session);
+
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ if (chain == mEffectChains[i]) {
+ mEffectChains.removeAt(i);
+ // detach all tracks with same session ID from this chain
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (session == track->sessionId()) {
+ track->setMainBuffer(mMixBuffer);
+ }
+ }
+ break;
+ }
+ }
+ return mEffectChains.size();
+}
+
+void AudioFlinger::PlaybackThread::lockEffectChains_l(
+ Vector<sp <AudioFlinger::EffectChain> >& effectChains)
+{
+ effectChains = mEffectChains;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->lock();
+ }
+}
+
+void AudioFlinger::PlaybackThread::unlockEffectChains(
+ Vector<sp <AudioFlinger::EffectChain> >& effectChains)
+{
+ for (size_t i = 0; i < effectChains.size(); i++) {
+ effectChains[i]->unlock();
+ }
+}
+
+
+sp<AudioFlinger::EffectModule> AudioFlinger::PlaybackThread::getEffect_l(int sessionId, int effectId)
+{
+ sp<EffectModule> effect;
+
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain != 0) {
+ effect = chain->getEffectFromId_l(effectId);
+ }
+ return effect;
+}
+
+status_t AudioFlinger::PlaybackThread::attachAuxEffect(
+ const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
+{
+ Mutex::Autolock _l(mLock);
+ return attachAuxEffect_l(track, EffectId);
+}
+
+status_t AudioFlinger::PlaybackThread::attachAuxEffect_l(
+ const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId)
+{
+ status_t status = NO_ERROR;
+
+ if (EffectId == 0) {
+ track->setAuxBuffer(0, NULL);
+ } else {
+ // Auxiliary effects are always in audio session AudioSystem::SESSION_OUTPUT_MIX
+ sp<EffectModule> effect = getEffect_l(AudioSystem::SESSION_OUTPUT_MIX, EffectId);
+ if (effect != 0) {
+ if ((effect->desc().flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ track->setAuxBuffer(EffectId, (int32_t *)effect->inBuffer());
+ } else {
+ status = INVALID_OPERATION;
+ }
+ } else {
+ status = BAD_VALUE;
+ }
+ }
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::detachAuxEffect_l(int effectId)
+{
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (track->auxEffectId() == effectId) {
+ attachAuxEffect_l(track, 0);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// EffectModule implementation
// ----------------------------------------------------------------------------
-void AudioFlinger::instantiate() {
- defaultServiceManager()->addService(
- String16("media.audio_flinger"), new AudioFlinger());
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectModule"
+
+AudioFlinger::EffectModule::EffectModule(const wp<ThreadBase>& wThread,
+ const wp<AudioFlinger::EffectChain>& chain,
+ effect_descriptor_t *desc,
+ int id,
+ int sessionId)
+ : mThread(wThread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL),
+ mStatus(NO_INIT), mState(IDLE)
+{
+ LOGV("Constructor %p", this);
+ int lStatus;
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return;
+ }
+ PlaybackThread *p = (PlaybackThread *)thread.get();
+
+ memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t));
+
+ // create effect engine from effect factory
+ mStatus = EffectCreate(&desc->uuid, sessionId, p->id(), &mEffectInterface);
+
+ if (mStatus != NO_ERROR) {
+ return;
+ }
+ lStatus = init();
+ if (lStatus < 0) {
+ mStatus = lStatus;
+ goto Error;
+ }
+
+ LOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
+ return;
+Error:
+ EffectRelease(mEffectInterface);
+ mEffectInterface = NULL;
+ LOGV("Constructor Error %d", mStatus);
+}
+
+AudioFlinger::EffectModule::~EffectModule()
+{
+ LOGV("Destructor %p", this);
+ if (mEffectInterface != NULL) {
+ // release effect engine
+ EffectRelease(mEffectInterface);
+ }
+}
+
+status_t AudioFlinger::EffectModule::addHandle(sp<EffectHandle>& handle)
+{
+ status_t status;
+
+ Mutex::Autolock _l(mLock);
+ // First handle in mHandles has highest priority and controls the effect module
+ int priority = handle->priority();
+ size_t size = mHandles.size();
+ sp<EffectHandle> h;
+ size_t i;
+ for (i = 0; i < size; i++) {
+ h = mHandles[i].promote();
+ if (h == 0) continue;
+ if (h->priority() <= priority) break;
+ }
+ // if inserted in first place, move effect control from previous owner to this handle
+ if (i == 0) {
+ if (h != 0) {
+ h->setControl(false, true);
+ }
+ handle->setControl(true, false);
+ status = NO_ERROR;
+ } else {
+ status = ALREADY_EXISTS;
+ }
+ mHandles.insertAt(handle, i);
+ return status;
+}
+
+size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle)
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mHandles.size();
+ size_t i;
+ for (i = 0; i < size; i++) {
+ if (mHandles[i] == handle) break;
+ }
+ if (i == size) {
+ return size;
+ }
+ mHandles.removeAt(i);
+ size = mHandles.size();
+ // if removed from first place, move effect control from this handle to next in line
+ if (i == 0 && size != 0) {
+ sp<EffectHandle> h = mHandles[0].promote();
+ if (h != 0) {
+ h->setControl(true, true);
+ }
+ }
+
+ return size;
+}
+
+void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle)
+{
+ // keep a strong reference on this EffectModule to avoid calling the
+ // destructor before we exit
+ sp<EffectModule> keep(this);
+ {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ playbackThread->disconnectEffect(keep, handle);
+ }
+ }
+}
+
+void AudioFlinger::EffectModule::updateState() {
+ Mutex::Autolock _l(mLock);
+
+ switch (mState) {
+ case RESTART:
+ reset_l();
+ // FALL THROUGH
+
+ case STARTING:
+ // clear auxiliary effect input buffer for next accumulation
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ memset(mConfig.inputCfg.buffer.raw,
+ 0,
+ mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
+ }
+ start_l();
+ mState = ACTIVE;
+ break;
+ case STOPPING:
+ stop_l();
+ mDisableWaitCnt = mMaxDisableWaitCnt;
+ mState = STOPPED;
+ break;
+ case STOPPED:
+ // mDisableWaitCnt is forced to 1 by process() when the engine indicates the end of the
+ // turn off sequence.
+ if (--mDisableWaitCnt == 0) {
+ reset_l();
+ mState = IDLE;
+ }
+ break;
+ default: //IDLE , ACTIVE
+ break;
+ }
+}
+
+void AudioFlinger::EffectModule::process()
+{
+ Mutex::Autolock _l(mLock);
+
+ if (mEffectInterface == NULL ||
+ mConfig.inputCfg.buffer.raw == NULL ||
+ mConfig.outputCfg.buffer.raw == NULL) {
+ return;
+ }
+
+ if (mState == ACTIVE || mState == STOPPING || mState == STOPPED || mState == RESTART) {
+ // 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,
+ mConfig.inputCfg.buffer.s32,
+ mConfig.inputCfg.buffer.frameCount/2);
+ }
+
+ // do the actual processing in the effect engine
+ int ret = (*mEffectInterface)->process(mEffectInterface,
+ &mConfig.inputCfg.buffer,
+ &mConfig.outputCfg.buffer);
+
+ // force transition to IDLE state when engine is ready
+ if (mState == STOPPED && ret == -ENODATA) {
+ mDisableWaitCnt = 1;
+ }
+
+ // clear auxiliary effect input buffer for next accumulation
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
+ }
+ } else if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT &&
+ mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw){
+ // If an insert effect is idle and input buffer is different from output buffer, copy input to
+ // output
+ sp<EffectChain> chain = mChain.promote();
+ if (chain != 0 && chain->activeTracks() != 0) {
+ size_t size = mConfig.inputCfg.buffer.frameCount * sizeof(int16_t);
+ if (mConfig.inputCfg.channels == CHANNEL_STEREO) {
+ size *= 2;
+ }
+ memcpy(mConfig.outputCfg.buffer.raw, mConfig.inputCfg.buffer.raw, size);
+ }
+ }
+}
+
+void AudioFlinger::EffectModule::reset_l()
+{
+ if (mEffectInterface == NULL) {
+ return;
+ }
+ (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);
+}
+
+status_t AudioFlinger::EffectModule::configure()
+{
+ uint32_t channels;
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return DEAD_OBJECT;
+ }
+
+ // TODO: handle configuration of effects replacing track process
+ if (thread->channelCount() == 1) {
+ channels = CHANNEL_MONO;
+ } else {
+ channels = CHANNEL_STEREO;
+ }
+
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ mConfig.inputCfg.channels = CHANNEL_MONO;
+ } else {
+ mConfig.inputCfg.channels = channels;
+ }
+ mConfig.outputCfg.channels = channels;
+ mConfig.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
+ mConfig.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
+ mConfig.inputCfg.samplingRate = thread->sampleRate();
+ mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
+ mConfig.inputCfg.bufferProvider.cookie = NULL;
+ mConfig.inputCfg.bufferProvider.getBuffer = NULL;
+ mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
+ mConfig.outputCfg.bufferProvider.cookie = NULL;
+ mConfig.outputCfg.bufferProvider.getBuffer = NULL;
+ mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
+ mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ // Insert effect:
+ // - in session AudioSystem::SESSION_OUTPUT_MIX or AudioSystem::SESSION_OUTPUT_STAGE,
+ // always overwrites output buffer: input buffer == output buffer
+ // - in other sessions:
+ // last effect in the chain accumulates in output buffer: input buffer != output buffer
+ // other effect: overwrites output buffer: input buffer == output buffer
+ // Auxiliary effect:
+ // accumulates in output buffer: input buffer != output buffer
+ // Therefore: accumulate <=> input buffer != output buffer
+ if (mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
+ mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+ } else {
+ mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+ }
+ mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
+ mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
+ mConfig.inputCfg.buffer.frameCount = thread->frameCount();
+ mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
+
+ LOGV("configure() %p thread %p buffer %p framecount %d",
+ this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
+
+ status_t cmdStatus;
+ uint32_t size = sizeof(int);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_CONFIGURE,
+ sizeof(effect_config_t),
+ &mConfig,
+ &size,
+ &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+
+ mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
+ (1000 * mConfig.outputCfg.buffer.frameCount);
+
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::init()
+{
+ Mutex::Autolock _l(mLock);
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_INIT,
+ 0,
+ NULL,
+ &size,
+ &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::start_l()
+{
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_ENABLE,
+ 0,
+ NULL,
+ &size,
+ &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::stop_l()
+{
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_DISABLE,
+ 0,
+ NULL,
+ &size,
+ &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData)
+{
+ Mutex::Autolock _l(mLock);
+// LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
+
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ cmdCode,
+ cmdSize,
+ pCmdData,
+ replySize,
+ pReplyData);
+ if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
+ uint32_t size = (replySize == NULL) ? 0 : *replySize;
+ for (size_t i = 1; i < mHandles.size(); i++) {
+ sp<EffectHandle> h = mHandles[i].promote();
+ if (h != 0) {
+ h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
+ }
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
+{
+ Mutex::Autolock _l(mLock);
+ LOGV("setEnabled %p enabled %d", this, enabled);
+
+ if (enabled != isEnabled()) {
+ switch (mState) {
+ // going from disabled to enabled
+ case IDLE:
+ mState = STARTING;
+ break;
+ case STOPPED:
+ mState = RESTART;
+ break;
+ case STOPPING:
+ mState = ACTIVE;
+ break;
+
+ // going from enabled to disabled
+ case RESTART:
+ case STARTING:
+ mState = IDLE;
+ break;
+ case ACTIVE:
+ mState = STOPPING;
+ break;
+ }
+ for (size_t i = 1; i < mHandles.size(); i++) {
+ sp<EffectHandle> h = mHandles[i].promote();
+ if (h != 0) {
+ h->setEnabled(enabled);
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+bool AudioFlinger::EffectModule::isEnabled()
+{
+ switch (mState) {
+ case RESTART:
+ case STARTING:
+ case ACTIVE:
+ return true;
+ case IDLE:
+ case STOPPING:
+ case STOPPED:
+ default:
+ return false;
+ }
+}
+
+status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
+{
+ Mutex::Autolock _l(mLock);
+ status_t status = NO_ERROR;
+
+ // Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume
+ // if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set)
+ if ((mState >= ACTIVE) &&
+ ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
+ (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) {
+ status_t cmdStatus;
+ uint32_t volume[2];
+ uint32_t *pVolume = NULL;
+ uint32_t size = sizeof(volume);
+ volume[0] = *left;
+ volume[1] = *right;
+ if (controller) {
+ pVolume = volume;
+ }
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_VOLUME,
+ size,
+ volume,
+ &size,
+ pVolume);
+ if (controller && status == NO_ERROR && size == sizeof(volume)) {
+ *left = volume[0];
+ *right = volume[1];
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
+{
+ Mutex::Autolock _l(mLock);
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
+ // convert device bit field from AudioSystem to EffectApi format.
+ device = deviceAudioSystemToEffectApi(device);
+ if (device == 0) {
+ return BAD_VALUE;
+ }
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_DEVICE,
+ sizeof(uint32_t),
+ &device,
+ &size,
+ &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
+{
+ Mutex::Autolock _l(mLock);
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
+ // convert audio mode from AudioSystem to EffectApi format.
+ int effectMode = modeAudioSystemToEffectApi(mode);
+ if (effectMode < 0) {
+ return BAD_VALUE;
+ }
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_AUDIO_MODE,
+ sizeof(int),
+ &effectMode,
+ &size,
+ &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ }
+ return status;
+}
+
+// update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
+const uint32_t AudioFlinger::EffectModule::sDeviceConvTable[] = {
+ DEVICE_EARPIECE, // AudioSystem::DEVICE_OUT_EARPIECE
+ DEVICE_SPEAKER, // AudioSystem::DEVICE_OUT_SPEAKER
+ DEVICE_WIRED_HEADSET, // case AudioSystem::DEVICE_OUT_WIRED_HEADSET
+ DEVICE_WIRED_HEADPHONE, // AudioSystem::DEVICE_OUT_WIRED_HEADPHONE
+ DEVICE_BLUETOOTH_SCO, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO
+ DEVICE_BLUETOOTH_SCO_HEADSET, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET
+ DEVICE_BLUETOOTH_SCO_CARKIT, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+ DEVICE_BLUETOOTH_A2DP, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP
+ DEVICE_BLUETOOTH_A2DP_HEADPHONES, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+ DEVICE_BLUETOOTH_A2DP_SPEAKER, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
+ DEVICE_AUX_DIGITAL // AudioSystem::DEVICE_OUT_AUX_DIGITAL
+};
+
+uint32_t AudioFlinger::EffectModule::deviceAudioSystemToEffectApi(uint32_t device)
+{
+ uint32_t deviceOut = 0;
+ while (device) {
+ const uint32_t i = 31 - __builtin_clz(device);
+ device &= ~(1 << i);
+ if (i >= sizeof(sDeviceConvTable)/sizeof(uint32_t)) {
+ LOGE("device convertion error for AudioSystem device 0x%08x", device);
+ return 0;
+ }
+ deviceOut |= (uint32_t)sDeviceConvTable[i];
+ }
+ return deviceOut;
+}
+
+// update this table when AudioSystem::audio_mode or audio_mode_e (in EffectApi.h) are modified
+const uint32_t AudioFlinger::EffectModule::sModeConvTable[] = {
+ AUDIO_MODE_NORMAL, // AudioSystem::MODE_NORMAL
+ AUDIO_MODE_RINGTONE, // AudioSystem::MODE_RINGTONE
+ AUDIO_MODE_IN_CALL // AudioSystem::MODE_IN_CALL
+};
+
+int AudioFlinger::EffectModule::modeAudioSystemToEffectApi(uint32_t mode)
+{
+ int modeOut = -1;
+ if (mode < sizeof(sModeConvTable) / sizeof(uint32_t)) {
+ modeOut = (int)sModeConvTable[mode];
+ }
+ return modeOut;
+}
+
+status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\tEffect ID %d:\n", mId);
+ result.append(buffer);
+
+ bool locked = tryLock(mLock);
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ result.append("\t\tCould not lock Fx mutex:\n");
+ }
+
+ result.append("\t\tSession Status State Engine:\n");
+ snprintf(buffer, SIZE, "\t\t%05d %03d %03d 0x%08x\n",
+ mSessionId, mStatus, mState, (uint32_t)mEffectInterface);
+ result.append(buffer);
+
+ result.append("\t\tDescriptor:\n");
+ snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
+ mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion,
+ mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1],mDescriptor.uuid.node[2],
+ mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
+ mDescriptor.type.timeLow, mDescriptor.type.timeMid, mDescriptor.type.timeHiAndVersion,
+ mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1],mDescriptor.type.node[2],
+ mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\t- apiVersion: %04X\n\t\t- flags: %08X\n",
+ mDescriptor.apiVersion,
+ mDescriptor.flags);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\t- name: %s\n",
+ mDescriptor.name);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\t- implementor: %s\n",
+ mDescriptor.implementor);
+ result.append(buffer);
+
+ result.append("\t\t- Input configuration:\n");
+ result.append("\t\t\tBuffer Frames Smp rate Channels Format\n");
+ snprintf(buffer, SIZE, "\t\t\t0x%08x %05d %05d %08x %d\n",
+ (uint32_t)mConfig.inputCfg.buffer.raw,
+ mConfig.inputCfg.buffer.frameCount,
+ mConfig.inputCfg.samplingRate,
+ mConfig.inputCfg.channels,
+ mConfig.inputCfg.format);
+ result.append(buffer);
+
+ result.append("\t\t- Output configuration:\n");
+ result.append("\t\t\tBuffer Frames Smp rate Channels Format\n");
+ snprintf(buffer, SIZE, "\t\t\t0x%08x %05d %05d %08x %d\n",
+ (uint32_t)mConfig.outputCfg.buffer.raw,
+ mConfig.outputCfg.buffer.frameCount,
+ mConfig.outputCfg.samplingRate,
+ mConfig.outputCfg.channels,
+ mConfig.outputCfg.format);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, "\t\t%d Clients:\n", mHandles.size());
+ result.append(buffer);
+ result.append("\t\t\tPid Priority Ctrl Locked client server\n");
+ for (size_t i = 0; i < mHandles.size(); ++i) {
+ sp<EffectHandle> handle = mHandles[i].promote();
+ if (handle != 0) {
+ handle->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+
+ result.append("\n");
+
+ write(fd, result.string(), result.length());
+
+ if (locked) {
+ mLock.unlock();
+ }
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+// EffectHandle implementation
+// ----------------------------------------------------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectHandle"
+
+AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority)
+ : BnEffect(),
+ mEffect(effect), mEffectClient(effectClient), mClient(client), mPriority(priority), mHasControl(false)
+{
+ LOGV("constructor %p", this);
+
+ int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
+ mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset);
+ if (mCblkMemory != 0) {
+ mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer());
+
+ if (mCblk) {
+ new(mCblk) effect_param_cblk_t();
+ mBuffer = (uint8_t *)mCblk + bufOffset;
+ }
+ } else {
+ LOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE + sizeof(effect_param_cblk_t));
+ return;
+ }
+}
+
+AudioFlinger::EffectHandle::~EffectHandle()
+{
+ LOGV("Destructor %p", this);
+ disconnect();
+}
+
+status_t AudioFlinger::EffectHandle::enable()
+{
+ if (!mHasControl) return INVALID_OPERATION;
+ if (mEffect == 0) return DEAD_OBJECT;
+
+ return mEffect->setEnabled(true);
+}
+
+status_t AudioFlinger::EffectHandle::disable()
+{
+ if (!mHasControl) return INVALID_OPERATION;
+ if (mEffect == NULL) return DEAD_OBJECT;
+
+ return mEffect->setEnabled(false);
+}
+
+void AudioFlinger::EffectHandle::disconnect()
+{
+ if (mEffect == 0) {
+ return;
+ }
+ mEffect->disconnect(this);
+ // release sp on module => module destructor can be called now
+ mEffect.clear();
+ if (mCblk) {
+ mCblk->~effect_param_cblk_t(); // destroy our shared-structure.
+ }
+ mCblkMemory.clear(); // and free the shared memory
+ if (mClient != 0) {
+ Mutex::Autolock _l(mClient->audioFlinger()->mLock);
+ mClient.clear();
+ }
+}
+
+status_t AudioFlinger::EffectHandle::command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData)
+{
+// LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p",
+// cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
+
+ // only get parameter command is permitted for applications not controlling the effect
+ if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
+ return INVALID_OPERATION;
+ }
+ if (mEffect == 0) return DEAD_OBJECT;
+
+ // handle commands that are not forwarded transparently to effect engine
+ if (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) {
+ // No need to trylock() here as this function is executed in the binder thread serving a particular client process:
+ // no risk to block the whole media server process or mixer threads is we are stuck here
+ Mutex::Autolock _l(mCblk->lock);
+ if (mCblk->clientIndex > EFFECT_PARAM_BUFFER_SIZE ||
+ mCblk->serverIndex > EFFECT_PARAM_BUFFER_SIZE) {
+ mCblk->serverIndex = 0;
+ mCblk->clientIndex = 0;
+ return BAD_VALUE;
+ }
+ status_t status = NO_ERROR;
+ while (mCblk->serverIndex < mCblk->clientIndex) {
+ int reply;
+ uint32_t rsize = sizeof(int);
+ int *p = (int *)(mBuffer + mCblk->serverIndex);
+ int size = *p++;
+ if (((uint8_t *)p + size) > mBuffer + mCblk->clientIndex) {
+ LOGW("command(): invalid parameter block size");
+ break;
+ }
+ effect_param_t *param = (effect_param_t *)p;
+ if (param->psize == 0 || param->vsize == 0) {
+ LOGW("command(): null parameter or value size");
+ mCblk->serverIndex += size;
+ continue;
+ }
+ uint32_t psize = sizeof(effect_param_t) +
+ ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) +
+ param->vsize;
+ status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM,
+ psize,
+ p,
+ &rsize,
+ &reply);
+ if (ret == NO_ERROR) {
+ if (reply != NO_ERROR) {
+ status = reply;
+ }
+ } else {
+ status = ret;
+ }
+ mCblk->serverIndex += size;
+ }
+ mCblk->serverIndex = 0;
+ mCblk->clientIndex = 0;
+ return status;
+ } else if (cmdCode == EFFECT_CMD_ENABLE) {
+ return enable();
+ } else if (cmdCode == EFFECT_CMD_DISABLE) {
+ return disable();
+ }
+
+ return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+}
+
+sp<IMemory> AudioFlinger::EffectHandle::getCblk() const {
+ return mCblkMemory;
+}
+
+void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal)
+{
+ LOGV("setControl %p control %d", this, hasControl);
+
+ mHasControl = hasControl;
+ if (signal && mEffectClient != 0) {
+ mEffectClient->controlStatusChanged(hasControl);
+ }
+}
+
+void AudioFlinger::EffectHandle::commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t replySize,
+ void *pReplyData)
+{
+ if (mEffectClient != 0) {
+ mEffectClient->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ }
+}
+
+
+
+void AudioFlinger::EffectHandle::setEnabled(bool enabled)
+{
+ if (mEffectClient != 0) {
+ mEffectClient->enableStatusChanged(enabled);
+ }
+}
+
+status_t AudioFlinger::EffectHandle::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnEffect::onTransact(code, data, reply, flags);
+}
+
+
+void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
+{
+ bool locked = tryLock(mCblk->lock);
+
+ snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n",
+ (mClient == NULL) ? getpid() : mClient->pid(),
+ mPriority,
+ mHasControl,
+ !locked,
+ mCblk->clientIndex,
+ mCblk->serverIndex
+ );
+
+ if (locked) {
+ mCblk->lock.unlock();
+ }
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectChain"
+
+AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
+ int sessionId)
+ : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mOwnInBuffer(false),
+ mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
+{
+ mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+}
+
+AudioFlinger::EffectChain::~EffectChain()
+{
+ if (mOwnInBuffer) {
+ delete mInBuffer;
+ }
+
+}
+
+// getEffectFromDesc_l() must be called with PlaybackThread::mLock held
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc_l(effect_descriptor_t *descriptor)
+{
+ sp<EffectModule> effect;
+ size_t size = mEffects.size();
+
+ for (size_t i = 0; i < size; i++) {
+ if (memcmp(&mEffects[i]->desc().uuid, &descriptor->uuid, sizeof(effect_uuid_t)) == 0) {
+ effect = mEffects[i];
+ break;
+ }
+ }
+ return effect;
+}
+
+// getEffectFromId_l() must be called with PlaybackThread::mLock held
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId_l(int id)
+{
+ sp<EffectModule> effect;
+ size_t size = mEffects.size();
+
+ for (size_t i = 0; i < size; i++) {
+ // by convention, return first effect if id provided is 0 (0 is never a valid id)
+ if (id == 0 || mEffects[i]->id() == id) {
+ effect = mEffects[i];
+ break;
+ }
+ }
+ return effect;
+}
+
+// Must be called with EffectChain::mLock locked
+void AudioFlinger::EffectChain::process_l()
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->process();
+ }
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->updateState();
+ }
+ // if no track is active, input buffer must be cleared here as the mixer process
+ // will not do it
+ if (mSessionId > 0 && activeTracks() == 0) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ size_t numSamples = thread->frameCount() * thread->channelCount();
+ memset(mInBuffer, 0, numSamples * sizeof(int16_t));
+ }
+ }
+}
+
+// addEffect_l() must be called with PlaybackThread::mLock held
+status_t AudioFlinger::EffectChain::addEffect_l(const sp<EffectModule>& effect)
+{
+ effect_descriptor_t desc = effect->desc();
+ uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
+
+ Mutex::Autolock _l(mLock);
+ effect->setChain(this);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return NO_INIT;
+ }
+ effect->setThread(thread);
+
+ if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ // Auxiliary effects are inserted at the beginning of mEffects vector as
+ // they are processed first and accumulated in chain input buffer
+ mEffects.insertAt(effect, 0);
+
+ // the input buffer for auxiliary effect contains mono samples in
+ // 32 bit format. This is to avoid saturation in AudoMixer
+ // accumulation stage. Saturation is done in EffectModule::process() before
+ // calling the process in effect engine
+ size_t numSamples = thread->frameCount();
+ int32_t *buffer = new int32_t[numSamples];
+ memset(buffer, 0, numSamples * sizeof(int32_t));
+ effect->setInBuffer((int16_t *)buffer);
+ // auxiliary effects output samples to chain input buffer for further processing
+ // by insert effects
+ effect->setOutBuffer(mInBuffer);
+ } else {
+ // Insert effects are inserted at the end of mEffects vector as they are processed
+ // after track and auxiliary effects.
+ // Insert effect order as a function of indicated preference:
+ // if EFFECT_FLAG_INSERT_EXCLUSIVE, insert in first position or reject if
+ // another effect is present
+ // else if EFFECT_FLAG_INSERT_FIRST, insert in first position or after the
+ // last effect claiming first position
+ // else if EFFECT_FLAG_INSERT_LAST, insert in last position or before the
+ // first effect claiming last position
+ // else if EFFECT_FLAG_INSERT_ANY insert after first or before last
+ // Reject insertion if an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is
+ // already present
+
+ int size = (int)mEffects.size();
+ int idx_insert = size;
+ int idx_insert_first = -1;
+ int idx_insert_last = -1;
+
+ for (int i = 0; i < size; i++) {
+ effect_descriptor_t d = mEffects[i]->desc();
+ uint32_t iMode = d.flags & EFFECT_FLAG_TYPE_MASK;
+ uint32_t iPref = d.flags & EFFECT_FLAG_INSERT_MASK;
+ if (iMode == EFFECT_FLAG_TYPE_INSERT) {
+ // check invalid effect chaining combinations
+ if (insertPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
+ iPref == EFFECT_FLAG_INSERT_EXCLUSIVE) {
+ LOGW("addEffect_l() could not insert effect %s: exclusive conflict with %s", desc.name, d.name);
+ return INVALID_OPERATION;
+ }
+ // remember position of first insert effect and by default
+ // select this as insert position for new effect
+ if (idx_insert == size) {
+ idx_insert = i;
+ }
+ // remember position of last insert effect claiming
+ // first position
+ if (iPref == EFFECT_FLAG_INSERT_FIRST) {
+ idx_insert_first = i;
+ }
+ // remember position of first insert effect claiming
+ // last position
+ if (iPref == EFFECT_FLAG_INSERT_LAST &&
+ idx_insert_last == -1) {
+ idx_insert_last = i;
+ }
+ }
+ }
+
+ // modify idx_insert from first position if needed
+ if (insertPref == EFFECT_FLAG_INSERT_LAST) {
+ if (idx_insert_last != -1) {
+ idx_insert = idx_insert_last;
+ } else {
+ idx_insert = size;
+ }
+ } else {
+ if (idx_insert_first != -1) {
+ idx_insert = idx_insert_first + 1;
+ }
+ }
+
+ // always read samples from chain input buffer
+ effect->setInBuffer(mInBuffer);
+
+ // if last effect in the chain, output samples to chain
+ // output buffer, otherwise to chain input buffer
+ if (idx_insert == size) {
+ if (idx_insert != 0) {
+ mEffects[idx_insert-1]->setOutBuffer(mInBuffer);
+ mEffects[idx_insert-1]->configure();
+ }
+ effect->setOutBuffer(mOutBuffer);
+ } else {
+ effect->setOutBuffer(mInBuffer);
+ }
+ mEffects.insertAt(effect, idx_insert);
+
+ LOGV("addEffect_l() effect %p, added in chain %p at rank %d", effect.get(), this, idx_insert);
+ }
+ effect->configure();
+ return NO_ERROR;
+}
+
+// removeEffect_l() must be called with PlaybackThread::mLock held
+size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
+{
+ Mutex::Autolock _l(mLock);
+ int size = (int)mEffects.size();
+ int i;
+ uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;
+
+ for (i = 0; i < size; i++) {
+ if (effect == mEffects[i]) {
+ if (type == EFFECT_FLAG_TYPE_AUXILIARY) {
+ delete[] effect->inBuffer();
+ } else {
+ if (i == size - 1 && i != 0) {
+ mEffects[i - 1]->setOutBuffer(mOutBuffer);
+ mEffects[i - 1]->configure();
+ }
+ }
+ mEffects.removeAt(i);
+ LOGV("removeEffect_l() effect %p, removed from chain %p at rank %d", effect.get(), this, i);
+ break;
+ }
+ }
+
+ return mEffects.size();
+}
+
+// setDevice_l() must be called with PlaybackThread::mLock held
+void AudioFlinger::EffectChain::setDevice_l(uint32_t device)
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->setDevice(device);
+ }
+}
+
+// setMode_l() must be called with PlaybackThread::mLock held
+void AudioFlinger::EffectChain::setMode_l(uint32_t mode)
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->setMode(mode);
+ }
+}
+
+// setVolume_l() must be called with PlaybackThread::mLock held
+bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right)
+{
+ uint32_t newLeft = *left;
+ uint32_t newRight = *right;
+ bool hasControl = false;
+ int ctrlIdx = -1;
+ size_t size = mEffects.size();
+
+ // first update volume controller
+ for (size_t i = size; i > 0; i--) {
+ if ((mEffects[i - 1]->state() >= EffectModule::ACTIVE) &&
+ (mEffects[i - 1]->desc().flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL) {
+ ctrlIdx = i - 1;
+ hasControl = true;
+ break;
+ }
+ }
+
+ if (ctrlIdx == mVolumeCtrlIdx && *left == mLeftVolume && *right == mRightVolume) {
+ if (hasControl) {
+ *left = mNewLeftVolume;
+ *right = mNewRightVolume;
+ }
+ return hasControl;
+ }
+
+ if (mVolumeCtrlIdx != -1) {
+ hasControl = true;
+ }
+ mVolumeCtrlIdx = ctrlIdx;
+ mLeftVolume = newLeft;
+ mRightVolume = newRight;
+
+ // second get volume update from volume controller
+ if (ctrlIdx >= 0) {
+ mEffects[ctrlIdx]->setVolume(&newLeft, &newRight, true);
+ mNewLeftVolume = newLeft;
+ mNewRightVolume = newRight;
+ }
+ // then indicate volume to all other effects in chain.
+ // Pass altered volume to effects before volume controller
+ // and requested volume to effects after controller
+ uint32_t lVol = newLeft;
+ uint32_t rVol = newRight;
+
+ for (size_t i = 0; i < size; i++) {
+ if ((int)i == ctrlIdx) continue;
+ // this also works for ctrlIdx == -1 when there is no volume controller
+ if ((int)i > ctrlIdx) {
+ lVol = *left;
+ rVol = *right;
+ }
+ mEffects[i]->setVolume(&lVol, &rVol, false);
+ }
+ *left = newLeft;
+ *right = newRight;
+
+ return hasControl;
+}
+
+status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "Effects for session %d:\n", mSessionId);
+ result.append(buffer);
+
+ bool locked = tryLock(mLock);
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ result.append("\tCould not lock mutex:\n");
+ }
+
+ result.append("\tNum fx In buffer Out buffer Active tracks:\n");
+ snprintf(buffer, SIZE, "\t%02d 0x%08x 0x%08x %d\n",
+ mEffects.size(),
+ (uint32_t)mInBuffer,
+ (uint32_t)mOutBuffer,
+ mActiveTrackCnt);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ for (size_t i = 0; i < mEffects.size(); ++i) {
+ sp<EffectModule> effect = mEffects[i];
+ if (effect != 0) {
+ effect->dump(fd, args);
+ }
+ }
+
+ if (locked) {
+ mLock.unlock();
+ }
+
+ return NO_ERROR;
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger"
+
+// ----------------------------------------------------------------------------
+
+status_t AudioFlinger::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnAudioFlinger::onTransact(code, data, reply, flags);
}
}; // namespace android
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 739ec33..51881f0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -31,10 +31,12 @@
#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/threads.h>
-#include <binder/MemoryDealer.h>
#include <utils/SortedVector.h>
#include <utils/Vector.h>
+#include <binder/BinderService.h>
+#include <binder/MemoryDealer.h>
+
#include <hardware_legacy/AudioHardwareInterface.h>
#include "AudioBufferProvider.h"
@@ -42,6 +44,7 @@
namespace android {
class audio_track_cblk_t;
+class effect_param_cblk_t;
class AudioMixer;
class AudioBuffer;
class AudioResampler;
@@ -57,10 +60,13 @@ class AudioResampler;
static const nsecs_t kStandbyTimeInNsecs = seconds(3);
-class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient
+class AudioFlinger :
+ public BinderService<AudioFlinger>,
+ public BnAudioFlinger
{
+ friend class BinderService<AudioFlinger>;
public:
- static void instantiate();
+ static char const* getServiceName() { return "media.audio_flinger"; }
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -75,6 +81,7 @@ public:
uint32_t flags,
const sp<IMemory>& sharedBuffer,
int output,
+ int *sessionId,
status_t *status);
virtual uint32_t sampleRate(int output) const;
@@ -139,8 +146,29 @@ public:
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output);
- // IBinder::DeathRecipient
- virtual void binderDied(const wp<IBinder>& who);
+ virtual int newAudioSessionId();
+
+ virtual status_t loadEffectLibrary(const char *libPath, int *handle);
+
+ virtual status_t unloadEffectLibrary(int handle);
+
+ virtual status_t queryNumberEffects(uint32_t *numEffects);
+
+ virtual status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor);
+
+ virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor);
+
+ virtual sp<IEffect> createEffect(pid_t pid,
+ effect_descriptor_t *pDesc,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ int output,
+ int sessionId,
+ status_t *status,
+ int *id,
+ int *enabled);
+
+ virtual status_t moveEffects(int session, int srcOutput, int dstOutput);
enum hardware_call_state {
AUDIO_HW_IDLE = 0,
@@ -170,6 +198,7 @@ public:
int channelCount,
int frameCount,
uint32_t flags,
+ int *sessionId,
status_t *status);
virtual status_t onTransact(
@@ -178,6 +207,8 @@ public:
Parcel* reply,
uint32_t flags);
+ uint32_t getMode() { return mMode; }
+
private:
AudioFlinger();
virtual ~AudioFlinger();
@@ -205,6 +236,27 @@ private:
pid_t mPid;
};
+ // --- Notification Client ---
+ class NotificationClient : public IBinder::DeathRecipient {
+ public:
+ NotificationClient(const sp<AudioFlinger>& audioFlinger,
+ const sp<IAudioFlingerClient>& client,
+ pid_t pid);
+ virtual ~NotificationClient();
+
+ sp<IAudioFlingerClient> client() { return mClient; }
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ NotificationClient(const NotificationClient&);
+ NotificationClient& operator = (const NotificationClient&);
+
+ sp<AudioFlinger> mAudioFlinger;
+ pid_t mPid;
+ sp<IAudioFlingerClient> mClient;
+ };
class TrackHandle;
class RecordHandle;
@@ -215,6 +267,9 @@ private:
class DuplicatingThread;
class Track;
class RecordTrack;
+ class EffectModule;
+ class EffectHandle;
+ class EffectChain;
class ThreadBase : public Thread {
public:
@@ -250,13 +305,15 @@ private:
int channelCount,
int frameCount,
uint32_t flags,
- const sp<IMemory>& sharedBuffer);
+ const sp<IMemory>& sharedBuffer,
+ int sessionId);
~TrackBase();
virtual status_t start() = 0;
virtual void stop() = 0;
sp<IMemory> getCblk() const;
audio_track_cblk_t* cblk() const { return mCblk; }
+ int sessionId() { return mSessionId; }
protected:
friend class ThreadBase;
@@ -305,6 +362,7 @@ private:
int mClientTid;
uint8_t mFormat;
uint32_t mFlags;
+ int mSessionId;
};
class ConfigEvent {
@@ -324,7 +382,7 @@ private:
virtual bool checkForNewParameters_l() = 0;
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys) = 0;
- virtual void audioConfigChanged(int event, int param = 0) = 0;
+ virtual void audioConfigChanged_l(int event, int param = 0) = 0;
void sendConfigEvent(int event, int param = 0);
void sendConfigEvent_l(int event, int param = 0);
void processConfigEvents();
@@ -348,9 +406,10 @@ private:
sp<AudioFlinger> mAudioFlinger;
uint32_t mSampleRate;
size_t mFrameCount;
- int mChannelCount;
+ uint32_t mChannels;
+ uint16_t mChannelCount;
+ uint16_t mFrameSize;
int mFormat;
- uint32_t mFrameSize;
Condition mParamCond;
Vector<String8> mNewParameters;
status_t mParamStatus;
@@ -386,7 +445,8 @@ private:
int format,
int channelCount,
int frameCount,
- const sp<IMemory>& sharedBuffer);
+ const sp<IMemory>& sharedBuffer,
+ int sessionId);
~Track();
void dump(char* buffer, size_t size);
@@ -405,6 +465,12 @@ private:
int type() const {
return mStreamType;
}
+ status_t attachAuxEffect(int EffectId);
+ void setAuxBuffer(int EffectId, int32_t *buffer);
+ int32_t *auxBuffer() { return mAuxBuffer; }
+ void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
+ int16_t *mainBuffer() { return mMainBuffer; }
+ int auxEffectId() { return mAuxEffectId; }
protected:
@@ -445,6 +511,9 @@ private:
bool mResetDone;
int mStreamType;
int mName;
+ int16_t *mMainBuffer;
+ int32_t *mAuxBuffer;
+ int mAuxEffectId;
}; // end of Track
@@ -486,7 +555,7 @@ private:
DuplicatingThread* mSourceThread;
}; // end of OutputTrack
- PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
virtual ~PlaybackThread();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -519,6 +588,7 @@ private:
int channelCount,
int frameCount,
const sp<IMemory>& sharedBuffer,
+ int sessionId,
status_t *status);
AudioStreamOut* getOutput() { return mOutput; }
@@ -528,8 +598,49 @@ private:
void restore() { if (mSuspended) mSuspended--; }
bool isSuspended() { return (mSuspended != 0); }
virtual String8 getParameters(const String8& keys);
- virtual void audioConfigChanged(int event, int param = 0);
+ virtual void audioConfigChanged_l(int event, int param = 0);
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
+ int16_t *mixBuffer() { return mMixBuffer; };
+
+ sp<EffectHandle> createEffect_l(
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority,
+ int sessionId,
+ effect_descriptor_t *desc,
+ int *enabled,
+ status_t *status);
+ void disconnectEffect(const sp< EffectModule>& effect,
+ const wp<EffectHandle>& handle);
+
+ // return values for hasAudioSession (bit field)
+ enum effect_state {
+ EFFECT_SESSION = 0x1, // the audio session corresponds to at least one
+ // effect
+ TRACK_SESSION = 0x2 // the audio session corresponds to at least one
+ // track
+ };
+
+ uint32_t hasAudioSession(int sessionId);
+ sp<EffectChain> getEffectChain(int sessionId);
+ sp<EffectChain> getEffectChain_l(int sessionId);
+ status_t addEffectChain_l(const sp<EffectChain>& chain);
+ size_t removeEffectChain_l(const sp<EffectChain>& chain);
+ void lockEffectChains_l(Vector<sp <EffectChain> >& effectChains);
+ void unlockEffectChains(Vector<sp <EffectChain> >& effectChains);
+
+ sp<AudioFlinger::EffectModule> getEffect_l(int sessionId, int effectId);
+ void detachAuxEffect_l(int effectId);
+ status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track,
+ int EffectId);
+ status_t attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track,
+ int EffectId);
+ void setMode(uint32_t mode);
+
+ status_t addEffect_l(const sp< EffectModule>& effect);
+ void removeEffect_l(const sp< EffectModule>& effect);
+
+ uint32_t getStrategyForSession_l(int sessionId);
struct stream_type_t {
stream_type_t()
@@ -553,6 +664,7 @@ private:
virtual void deleteTrackName_l(int name) = 0;
virtual uint32_t activeSleepTimeUs() = 0;
virtual uint32_t idleSleepTimeUs() = 0;
+ virtual uint32_t suspendSleepTimeUs() = 0;
private:
@@ -572,8 +684,11 @@ private:
void readOutputParameters();
+ uint32_t device() { return mDevice; }
+
virtual status_t dumpInternals(int fd, const Vector<String16>& args);
status_t dumpTracks(int fd, const Vector<String16>& args);
+ status_t dumpEffectChains(int fd, const Vector<String16>& args);
SortedVector< sp<Track> > mTracks;
// mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
@@ -584,30 +699,33 @@ private:
int mNumWrites;
int mNumDelayedWrites;
bool mInWrite;
+ Vector< sp<EffectChain> > mEffectChains;
+ uint32_t mDevice;
};
class MixerThread : public PlaybackThread {
public:
- MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ MixerThread (const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output,
+ int id,
+ uint32_t device);
virtual ~MixerThread();
// Thread virtuals
virtual bool threadLoop();
- void getTracks(SortedVector < sp<Track> >& tracks,
- SortedVector < wp<Track> >& activeTracks,
- int streamType);
- void putTracks(SortedVector < sp<Track> >& tracks,
- SortedVector < wp<Track> >& activeTracks);
+ void invalidateTracks(int 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, Vector< sp<Track> > *tracksToRemove);
+ uint32_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks,
+ Vector< sp<Track> > *tracksToRemove);
virtual int getTrackName_l();
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs();
virtual uint32_t idleSleepTimeUs();
+ virtual uint32_t suspendSleepTimeUs();
AudioMixer* mAudioMixer;
};
@@ -615,7 +733,7 @@ private:
class DirectOutputThread : public PlaybackThread {
public:
- DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
~DirectOutputThread();
// Thread virtuals
@@ -628,10 +746,15 @@ private:
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs();
virtual uint32_t idleSleepTimeUs();
+ virtual uint32_t suspendSleepTimeUs();
private:
- float mLeftVolume;
- float mRightVolume;
+ void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
+
+ float mLeftVolFloat;
+ float mRightVolFloat;
+ uint16_t mLeftVolShort;
+ uint16_t mRightVolShort;
};
class DuplicatingThread : public MixerThread {
@@ -661,6 +784,12 @@ private:
float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
void audioConfigChanged_l(int event, int ioHandle, void *param2);
+ int nextUniqueId();
+ status_t moveEffectChain_l(int session,
+ AudioFlinger::PlaybackThread *srcThread,
+ AudioFlinger::PlaybackThread *dstThread,
+ bool reRegister);
+
friend class AudioBuffer;
class TrackHandle : public android::BnAudioTrack {
@@ -674,6 +803,7 @@ private:
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:
@@ -685,6 +815,7 @@ private:
void removeClient_l(pid_t pid);
+ void removeNotificationClient(pid_t pid);
// record thread
@@ -701,7 +832,8 @@ private:
int format,
int channelCount,
int frameCount,
- uint32_t flags);
+ uint32_t flags,
+ int sessionId);
~RecordTrack();
virtual status_t start();
@@ -744,7 +876,7 @@ private:
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
virtual bool checkForNewParameters_l();
virtual String8 getParameters(const String8& keys);
- virtual void audioConfigChanged(int event, int param = 0);
+ virtual void audioConfigChanged_l(int event, int param = 0);
void readInputParameters();
virtual unsigned int getInputFramesLost();
@@ -776,6 +908,259 @@ private:
sp<RecordThread::RecordTrack> mRecordTrack;
};
+ //--- Audio Effect Management
+
+ // EffectModule and EffectChain classes both have their own mutex to protect
+ // state changes or resource modifications. Always respect the following order
+ // if multiple mutexes must be acquired to avoid cross deadlock:
+ // AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
+
+ // The EffectModule class is a wrapper object controlling the effect engine implementation
+ // in the effect library. It prevents concurrent calls to process() and command() functions
+ // from different client threads. It keeps a list of EffectHandle objects corresponding
+ // to all client applications using this effect and notifies applications of effect state,
+ // control or parameter changes. It manages the activation state machine to send appropriate
+ // reset, enable, disable commands to effect engine and provide volume
+ // ramping when effects are activated/deactivated.
+ // When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
+ // the attached track(s) to accumulate their auxiliary channel.
+ class EffectModule: public RefBase {
+ public:
+ EffectModule(const wp<ThreadBase>& wThread,
+ const wp<AudioFlinger::EffectChain>& chain,
+ effect_descriptor_t *desc,
+ int id,
+ int sessionId);
+ ~EffectModule();
+
+ enum effect_state {
+ IDLE,
+ RESTART,
+ STARTING,
+ ACTIVE,
+ STOPPING,
+ STOPPED
+ };
+
+ int id() { return mId; }
+ void process();
+ void updateState();
+ status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
+
+ void reset_l();
+ status_t configure();
+ status_t init();
+ uint32_t state() {
+ return mState;
+ }
+ uint32_t status() {
+ return mStatus;
+ }
+ int sessionId() {
+ return mSessionId;
+ }
+ status_t setEnabled(bool enabled);
+ bool isEnabled();
+
+ void setInBuffer(int16_t *buffer) { mConfig.inputCfg.buffer.s16 = buffer; }
+ int16_t *inBuffer() { return mConfig.inputCfg.buffer.s16; }
+ void setOutBuffer(int16_t *buffer) { mConfig.outputCfg.buffer.s16 = buffer; }
+ int16_t *outBuffer() { return mConfig.outputCfg.buffer.s16; }
+ void setChain(const wp<EffectChain>& chain) { mChain = chain; }
+ void setThread(const wp<ThreadBase>& thread) { mThread = thread; }
+
+ status_t addHandle(sp<EffectHandle>& handle);
+ void disconnect(const wp<EffectHandle>& handle);
+ size_t removeHandle (const wp<EffectHandle>& handle);
+
+ effect_descriptor_t& desc() { return mDescriptor; }
+ wp<EffectChain>& chain() { return mChain; }
+
+ 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 dump(int fd, const Vector<String16>& args);
+
+ protected:
+
+ // Maximum time allocated to effect engines to complete the turn off sequence
+ static const uint32_t MAX_DISABLE_TIME_MS = 10000;
+
+ EffectModule(const EffectModule&);
+ EffectModule& operator = (const EffectModule&);
+
+ status_t start_l();
+ status_t stop_l();
+
+ // update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
+ static const uint32_t sDeviceConvTable[];
+ static uint32_t deviceAudioSystemToEffectApi(uint32_t device);
+
+ // update this table when AudioSystem::audio_mode or audio_mode_e (in EffectApi.h) are modified
+ static const uint32_t sModeConvTable[];
+ static int modeAudioSystemToEffectApi(uint32_t mode);
+
+ 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
+ int mSessionId; // audio session ID
+ effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
+ effect_config_t mConfig; // input and output audio configuration
+ effect_interface_t mEffectInterface; // Effect module C API
+ status_t mStatus; // initialization status
+ uint32_t mState; // current activation state (effect_state)
+ Vector< wp<EffectHandle> > mHandles; // list of client handles
+ uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after
+ // sending disable command.
+ uint32_t mDisableWaitCnt; // current process() calls count during disable period.
+ };
+
+ // The EffectHandle class implements the IEffect interface. It provides resources
+ // to receive parameter updates, keeps track of effect control
+ // ownership and state and has a pointer to the EffectModule object it is controlling.
+ // There is one EffectHandle object for each application controlling (or using)
+ // an effect module.
+ // The EffectHandle is obtained by calling AudioFlinger::createEffect().
+ class EffectHandle: public android::BnEffect {
+ public:
+
+ EffectHandle(const sp<EffectModule>& effect,
+ const sp<AudioFlinger::Client>& client,
+ const sp<IEffectClient>& effectClient,
+ int32_t priority);
+ virtual ~EffectHandle();
+
+ // IEffect
+ virtual status_t enable();
+ virtual status_t disable();
+ virtual status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
+ virtual void disconnect();
+ virtual sp<IMemory> getCblk() const;
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+
+ // Give or take control of effect module
+ void setControl(bool hasControl, bool signal);
+ void commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t replySize,
+ void *pReplyData);
+ void setEnabled(bool enabled);
+
+ // Getters
+ int id() { return mEffect->id(); }
+ int priority() { return mPriority; }
+ bool hasControl() { return mHasControl; }
+ sp<EffectModule> effect() { return mEffect; }
+
+ void dump(char* buffer, size_t size);
+
+ protected:
+
+ EffectHandle(const EffectHandle&);
+ EffectHandle& operator =(const EffectHandle&);
+
+ sp<EffectModule> mEffect; // pointer to controlled EffectModule
+ sp<IEffectClient> mEffectClient; // callback interface for client notifications
+ sp<Client> mClient; // client for shared memory allocation
+ sp<IMemory> mCblkMemory; // shared memory for control block
+ effect_param_cblk_t* mCblk; // control block for deferred parameter setting via shared memory
+ uint8_t* mBuffer; // pointer to parameter area in shared memory
+ int mPriority; // client application priority to control the effect
+ bool mHasControl; // true if this handle is controlling the effect
+ };
+
+ // the EffectChain class represents a group of effects associated to one audio session.
+ // There can be any number of EffectChain objects per output mixer thread (PlaybackThread).
+ // The EffecChain with session ID 0 contains global effects applied to the output mix.
+ // Effects in this chain can be insert or auxiliary. Effects in other chains (attached to tracks)
+ // are insert only. The EffectChain maintains an ordered list of effect module, the order corresponding
+ // in the effect process order. When attached to a track (session ID != 0), it also provide it's own
+ // input buffer used by the track as accumulation buffer.
+ class EffectChain: public RefBase {
+ public:
+ EffectChain(const wp<ThreadBase>& wThread, int sessionId);
+ ~EffectChain();
+
+ void process_l();
+
+ void lock() {
+ mLock.lock();
+ }
+ void unlock() {
+ mLock.unlock();
+ }
+
+ status_t addEffect_l(const sp<EffectModule>& handle);
+ size_t removeEffect_l(const sp<EffectModule>& handle);
+
+ int sessionId() {
+ return mSessionId;
+ }
+
+ sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor);
+ sp<EffectModule> getEffectFromId_l(int id);
+ bool setVolume_l(uint32_t *left, uint32_t *right);
+ void setDevice_l(uint32_t device);
+ void setMode_l(uint32_t mode);
+
+ void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
+ mInBuffer = buffer;
+ mOwnInBuffer = ownsBuffer;
+ }
+ int16_t *inBuffer() {
+ return mInBuffer;
+ }
+ void setOutBuffer(int16_t *buffer) {
+ mOutBuffer = buffer;
+ }
+ int16_t *outBuffer() {
+ return mOutBuffer;
+ }
+
+ void startTrack() {mActiveTrackCnt++;}
+ void stopTrack() {mActiveTrackCnt--;}
+ int activeTracks() { return mActiveTrackCnt;}
+
+ uint32_t strategy() { return mStrategy; }
+ void setStrategy(uint32_t strategy)
+ { mStrategy = strategy; }
+
+ status_t dump(int fd, const Vector<String16>& args);
+
+ protected:
+
+ EffectChain(const EffectChain&);
+ EffectChain& operator =(const EffectChain&);
+
+ wp<ThreadBase> mThread; // parent mixer thread
+ Mutex mLock; // mutex protecting effect list
+ Vector<sp<EffectModule> > mEffects; // list of effect modules
+ int mSessionId; // audio session ID
+ int16_t *mInBuffer; // chain input buffer
+ int16_t *mOutBuffer; // chain output buffer
+ int mActiveTrackCnt; // number of active tracks connected
+ bool mOwnInBuffer; // true if the chain owns its input buffer
+ int mVolumeCtrlIdx; // index of insert effect having control over volume
+ uint32_t mLeftVolume; // previous volume on left channel
+ uint32_t mRightVolume; // previous volume on right channel
+ uint32_t mNewLeftVolume; // new volume on left channel
+ uint32_t mNewRightVolume; // new volume on right channel
+ uint32_t mStrategy; // strategy for this effect chain
+ };
+
friend class RecordThread;
friend class PlaybackThread;
@@ -796,8 +1181,13 @@ private:
DefaultKeyedVector< int, sp<RecordThread> > mRecordThreads;
- SortedVector< sp<IBinder> > mNotificationClients;
- int mNextThreadId;
+ DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients;
+ volatile int32_t mNextUniqueId;
+#ifdef LVMX
+ int mLifeVibesClientPid;
+#endif
+ uint32_t mMode;
+
};
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 19a442a..8aaa325 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -56,6 +56,8 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate)
t->volume[1] = UNITY_GAIN;
t->volumeInc[0] = 0;
t->volumeInc[1] = 0;
+ t->auxLevel = 0;
+ t->auxInc = 0;
t->channelCount = 2;
t->enabled = 0;
t->format = 16;
@@ -65,6 +67,8 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate)
t->resampler = 0;
t->sampleRate = mSampleRate;
t->in = 0;
+ t->mainBuffer = NULL;
+ t->auxBuffer = NULL;
t++;
}
}
@@ -169,28 +173,48 @@ status_t AudioMixer::setActiveTrack(int track)
return NO_ERROR;
}
-status_t AudioMixer::setParameter(int target, int name, int value)
+status_t AudioMixer::setParameter(int target, int name, void *value)
{
+ int valueInt = (int)value;
+ int32_t *valueBuf = (int32_t *)value;
+
switch (target) {
case TRACK:
if (name == CHANNEL_COUNT) {
- if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) {
- if (mState.tracks[ mActiveTrack ].channelCount != value) {
- mState.tracks[ mActiveTrack ].channelCount = value;
- LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value);
+ if ((uint32_t(valueInt) <= MAX_NUM_CHANNELS) && (valueInt)) {
+ if (mState.tracks[ mActiveTrack ].channelCount != valueInt) {
+ mState.tracks[ mActiveTrack ].channelCount = valueInt;
+ LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", valueInt);
invalidateState(1<<mActiveTrack);
}
return NO_ERROR;
}
}
+ if (name == MAIN_BUFFER) {
+ if (mState.tracks[ mActiveTrack ].mainBuffer != valueBuf) {
+ mState.tracks[ mActiveTrack ].mainBuffer = valueBuf;
+ LOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ }
+ if (name == AUX_BUFFER) {
+ if (mState.tracks[ mActiveTrack ].auxBuffer != valueBuf) {
+ mState.tracks[ mActiveTrack ].auxBuffer = valueBuf;
+ LOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ }
+
break;
case RESAMPLE:
if (name == SAMPLE_RATE) {
- if (value > 0) {
+ if (valueInt > 0) {
track_t& track = mState.tracks[ mActiveTrack ];
- if (track.setResampler(uint32_t(value), mSampleRate)) {
+ if (track.setResampler(uint32_t(valueInt), mSampleRate)) {
LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
- uint32_t(value));
+ uint32_t(valueInt));
invalidateState(1<<mActiveTrack);
}
return NO_ERROR;
@@ -201,18 +225,39 @@ status_t AudioMixer::setParameter(int target, int name, int value)
case VOLUME:
if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) {
track_t& track = mState.tracks[ mActiveTrack ];
- if (track.volume[name-VOLUME0] != value) {
+ if (track.volume[name-VOLUME0] != valueInt) {
+ LOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16;
- track.volume[name-VOLUME0] = value;
+ track.volume[name-VOLUME0] = valueInt;
if (target == VOLUME) {
- track.prevVolume[name-VOLUME0] = value << 16;
+ track.prevVolume[name-VOLUME0] = valueInt << 16;
track.volumeInc[name-VOLUME0] = 0;
} else {
- int32_t d = (value<<16) - track.prevVolume[name-VOLUME0];
+ int32_t d = (valueInt<<16) - track.prevVolume[name-VOLUME0];
int32_t volInc = d / int32_t(mState.frameCount);
track.volumeInc[name-VOLUME0] = volInc;
if (volInc == 0) {
- track.prevVolume[name-VOLUME0] = value << 16;
+ track.prevVolume[name-VOLUME0] = valueInt << 16;
+ }
+ }
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ } else if (name == AUXLEVEL) {
+ track_t& track = mState.tracks[ mActiveTrack ];
+ if (track.auxLevel != valueInt) {
+ LOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
+ track.prevAuxLevel = track.auxLevel << 16;
+ track.auxLevel = valueInt;
+ if (target == VOLUME) {
+ track.prevAuxLevel = valueInt << 16;
+ track.auxInc = 0;
+ } else {
+ int32_t d = (valueInt<<16) - track.prevAuxLevel;
+ int32_t volInc = d / int32_t(mState.frameCount);
+ track.auxInc = volInc;
+ if (volInc == 0) {
+ track.prevAuxLevel = valueInt << 16;
}
}
invalidateState(1<<mActiveTrack);
@@ -245,7 +290,7 @@ bool AudioMixer::track_t::doesResample() const
}
inline
-void AudioMixer::track_t::adjustVolumeRamp()
+void AudioMixer::track_t::adjustVolumeRamp(bool aux)
{
for (int i=0 ; i<2 ; i++) {
if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
@@ -254,6 +299,13 @@ void AudioMixer::track_t::adjustVolumeRamp()
prevVolume[i] = volume[i]<<16;
}
}
+ if (aux) {
+ if (((auxInc>0) && (((prevAuxLevel+auxInc)>>16) >= auxLevel)) ||
+ ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) {
+ auxInc = 0;
+ prevAuxLevel = auxLevel<<16;
+ }
+ }
}
@@ -265,13 +317,13 @@ status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer)
-void AudioMixer::process(void* output)
+void AudioMixer::process()
{
- mState.hook(&mState, output);
+ mState.hook(&mState);
}
-void AudioMixer::process__validate(state_t* state, void* output)
+void AudioMixer::process__validate(state_t* state)
{
LOGW_IF(!state->needsChanged,
"in process__validate() but nothing's invalid");
@@ -308,7 +360,10 @@ void AudioMixer::process__validate(state_t* state, void* output)
n |= NEEDS_CHANNEL_1 + t.channelCount - 1;
n |= NEEDS_FORMAT_16;
n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED;
-
+ if (t.auxLevel != 0 && t.auxBuffer != NULL) {
+ n |= NEEDS_AUX_ENABLED;
+ }
+
if (t.volumeInc[0]|t.volumeInc[1]) {
volumeRamp = 1;
} else if (!t.doesResample() && t.volumeRL == 0) {
@@ -319,6 +374,9 @@ void AudioMixer::process__validate(state_t* state, void* output)
if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) {
t.hook = track__nop;
} else {
+ if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ all16BitsStereoNoResample = 0;
+ }
if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
all16BitsStereoNoResample = 0;
resampling = 1;
@@ -369,7 +427,7 @@ void AudioMixer::process__validate(state_t* state, void* output)
countActiveTracks, state->enabledTracks,
all16BitsStereoNoResample, resampling, volumeRamp);
- state->hook(state, output);
+ state->hook(state);
// Now that the volume ramp has been done, set optimal state and
// track hooks for subsequent mixer process
@@ -390,7 +448,7 @@ void AudioMixer::process__validate(state_t* state, void* output)
}
if (allMuted) {
state->hook = process__nop;
- } else if (!resampling && all16BitsStereoNoResample) {
+ } else if (all16BitsStereoNoResample) {
if (countActiveTracks == 1) {
state->hook = process__OneTrack16BitsStereoNoResampling;
}
@@ -481,30 +539,44 @@ int32_t mulRL(int left, uint32_t inRL, uint32_t vRL)
}
-void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
+void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
{
t->resampler->setSampleRate(t->sampleRate);
// ramp gain - resample to temp buffer and scale/mix in 2nd step
- if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ if (aux != NULL) {
+ // always resample with unity gain when sending to auxiliary buffer to be able
+ // to apply send level after resampling
+ // TODO: modify each resampler to support aux channel?
t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
- volumeRampStereo(t, out, outFrameCount, temp);
- }
+ if 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]) {
+ t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
+ memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
+ t->resampler->resample(temp, outFrameCount, t->bufferProvider);
+ volumeRampStereo(t, out, outFrameCount, temp, aux);
+ }
- // constant gain
- else {
- t->resampler->setVolume(t->volume[0], t->volume[1]);
- t->resampler->resample(out, outFrameCount, t->bufferProvider);
+ // constant gain
+ else {
+ t->resampler->setVolume(t->volume[0], t->volume[1]);
+ t->resampler->resample(out, outFrameCount, t->bufferProvider);
+ }
}
}
-void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
+void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
{
}
-void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
int32_t vl = t->prevVolume[0];
int32_t vr = t->prevVolume[1];
@@ -514,98 +586,238 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i
//LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
// (vl + vlInc*frameCount)/65536.0f, frameCount);
-
+
// ramp volume
- do {
- *out++ += (vl >> 16) * (*temp++ >> 12);
- *out++ += (vr >> 16) * (*temp++ >> 12);
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
+ if UNLIKELY(aux != NULL) {
+ int32_t va = t->prevAuxLevel;
+ const int32_t vaInc = t->auxInc;
+ int32_t l;
+ int32_t r;
+ do {
+ l = (*temp++ >> 12);
+ r = (*temp++ >> 12);
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * r;
+ *aux++ += (va >> 17) * (l + r);
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+ t->prevAuxLevel = va;
+ } else {
+ do {
+ *out++ += (vl >> 16) * (*temp++ >> 12);
+ *out++ += (vr >> 16) * (*temp++ >> 12);
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+ }
t->prevVolume[0] = vl;
t->prevVolume[1] = vr;
- t->adjustVolumeRamp();
+ t->adjustVolumeRamp((aux != NULL));
}
-void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+void AudioMixer::volumeStereo(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);
-
- // ramp gain
- if 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];
- const int32_t vrInc = t->volumeInc[1];
-
- // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
+ const int16_t vl = t->volume[0];
+ const int16_t vr = t->volume[1];
+ if UNLIKELY(aux != NULL) {
+ const int16_t va = (int16_t)t->auxLevel;
do {
- *out++ += (vl >> 16) * (int32_t) *in++;
- *out++ += (vr >> 16) * (int32_t) *in++;
- vl += vlInc;
- vr += vrInc;
+ int16_t l = (int16_t)(*temp++ >> 12);
+ int16_t r = (int16_t)(*temp++ >> 12);
+ out[0] = mulAdd(l, vl, out[0]);
+ int16_t a = (int16_t)(((int32_t)l + r) >> 1);
+ out[1] = mulAdd(r, vr, out[1]);
+ out += 2;
+ aux[0] = mulAdd(a, va, aux[0]);
+ aux++;
} while (--frameCount);
-
- t->prevVolume[0] = vl;
- t->prevVolume[1] = vr;
- t->adjustVolumeRamp();
- }
-
- // constant gain
- else {
- const uint32_t vrl = t->volumeRL;
+ } else {
do {
- uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
- in += 2;
- out[0] = mulAddRL(1, rl, vrl, out[0]);
- out[1] = mulAddRL(0, rl, vrl, out[1]);
+ int16_t l = (int16_t)(*temp++ >> 12);
+ int16_t r = (int16_t)(*temp++ >> 12);
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(r, vr, out[1]);
out += 2;
} while (--frameCount);
}
+}
+
+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);
+
+ if UNLIKELY(aux != NULL) {
+ int32_t l;
+ int32_t r;
+ // ramp gain
+ if 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;
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+ const int32_t vaInc = t->auxInc;
+ // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ l = (int32_t)*in++;
+ r = (int32_t)*in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * r;
+ *aux++ += (va >> 17) * (l + r);
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->prevAuxLevel = va;
+ t->adjustVolumeRamp(true);
+ }
+
+ // constant gain
+ else {
+ const uint32_t vrl = t->volumeRL;
+ const int16_t va = (int16_t)t->auxLevel;
+ do {
+ uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1);
+ in += 2;
+ out[0] = mulAddRL(1, rl, vrl, out[0]);
+ out[1] = mulAddRL(0, rl, vrl, out[1]);
+ out += 2;
+ aux[0] = mulAdd(a, va, aux[0]);
+ aux++;
+ } while (--frameCount);
+ }
+ } else {
+ // ramp gain
+ if 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];
+ const int32_t vrInc = t->volumeInc[1];
+
+ // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ *out++ += (vl >> 16) * (int32_t) *in++;
+ *out++ += (vr >> 16) * (int32_t) *in++;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->adjustVolumeRamp(false);
+ }
+
+ // constant gain
+ else {
+ const uint32_t vrl = t->volumeRL;
+ do {
+ uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ in += 2;
+ out[0] = mulAddRL(1, rl, vrl, out[0]);
+ out[1] = mulAddRL(0, rl, vrl, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
+ }
t->in = in;
}
-void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+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);
- // ramp gain
- if 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];
- const int32_t vrInc = t->volumeInc[1];
+ if UNLIKELY(aux != NULL) {
+ // ramp gain
+ if 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;
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+ const int32_t vaInc = t->auxInc;
- // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
+ // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
- do {
- int32_t l = *in++;
- *out++ += (vl >> 16) * l;
- *out++ += (vr >> 16) * l;
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
-
- t->prevVolume[0] = vl;
- t->prevVolume[1] = vr;
- t->adjustVolumeRamp();
- }
- // constant gain
- else {
- const int16_t vl = t->volume[0];
- const int16_t vr = t->volume[1];
- do {
- int16_t l = *in++;
- out[0] = mulAdd(l, vl, out[0]);
- out[1] = mulAdd(l, vr, out[1]);
- out += 2;
- } while (--frameCount);
+ do {
+ int32_t l = *in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * l;
+ *aux++ += (va >> 16) * l;
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->prevAuxLevel = va;
+ t->adjustVolumeRamp(true);
+ }
+ // constant gain
+ else {
+ const int16_t vl = t->volume[0];
+ const int16_t vr = t->volume[1];
+ const int16_t va = (int16_t)t->auxLevel;
+ do {
+ int16_t l = *in++;
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(l, vr, out[1]);
+ out += 2;
+ aux[0] = mulAdd(l, va, aux[0]);
+ aux++;
+ } while (--frameCount);
+ }
+ } else {
+ // ramp gain
+ if 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];
+ const int32_t vrInc = t->volumeInc[1];
+
+ // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ int32_t l = *in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * l;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->adjustVolumeRamp(false);
+ }
+ // constant gain
+ else {
+ const int16_t vl = t->volume[0];
+ const int16_t vr = t->volume[1];
+ do {
+ int16_t l = *in++;
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(l, vr, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
}
t->in = in;
}
@@ -624,37 +836,56 @@ void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c)
}
// no-op case
-void AudioMixer::process__nop(state_t* state, void* output)
+void AudioMixer::process__nop(state_t* state)
{
- // this assumes output 16 bits stereo, no resampling
- memset(output, 0, state->frameCount*4);
- uint32_t en = state->enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
- track_t& t = state->tracks[i];
- size_t outFrames = state->frameCount;
- while (outFrames) {
- t.buffer.frameCount = outFrames;
- t.bufferProvider->getNextBuffer(&t.buffer);
- if (!t.buffer.raw) break;
- outFrames -= t.buffer.frameCount;
- t.bufferProvider->releaseBuffer(&t.buffer);
+ uint32_t e0 = state->enabledTracks;
+ size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS;
+ while (e0) {
+ // process by group of tracks with same output buffer to
+ // avoid multiple memset() on same buffer
+ uint32_t e1 = e0, e2 = e0;
+ int i = 31 - __builtin_clz(e1);
+ track_t& t1 = state->tracks[i];
+ e2 &= ~(1<<i);
+ while (e2) {
+ i = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<i);
+ track_t& t2 = state->tracks[i];
+ if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ e1 &= ~(1<<i);
+ }
+ }
+ e0 &= ~(e1);
+
+ memset(t1.mainBuffer, 0, bufSize);
+
+ while (e1) {
+ i = 31 - __builtin_clz(e1);
+ e1 &= ~(1<<i);
+ t1 = state->tracks[i];
+ size_t outFrames = state->frameCount;
+ while (outFrames) {
+ t1.buffer.frameCount = outFrames;
+ t1.bufferProvider->getNextBuffer(&t1.buffer);
+ if (!t1.buffer.raw) break;
+ outFrames -= t1.buffer.frameCount;
+ t1.bufferProvider->releaseBuffer(&t1.buffer);
+ }
}
}
}
// generic code without resampling
-void AudioMixer::process__genericNoResampling(state_t* state, void* output)
+void AudioMixer::process__genericNoResampling(state_t* state)
{
int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
// acquire each track's buffer
uint32_t enabledTracks = state->enabledTracks;
- uint32_t en = enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
+ uint32_t e0 = enabledTracks;
+ while (e0) {
+ const int i = 31 - __builtin_clz(e0);
+ e0 &= ~(1<<i);
track_t& t = state->tracks[i];
t.buffer.frameCount = state->frameCount;
t.bufferProvider->getNextBuffer(&t.buffer);
@@ -666,110 +897,156 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output)
enabledTracks &= ~(1<<i);
}
- // this assumes output 16 bits stereo, no resampling
- int32_t* out = static_cast<int32_t*>(output);
- size_t numFrames = state->frameCount;
- do {
- memset(outTemp, 0, sizeof(outTemp));
-
- en = enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
- track_t& t = state->tracks[i];
- size_t outFrames = BLOCKSIZE;
-
- while (outFrames) {
- size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
- if (inFrames) {
- (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp);
- t.frameCount -= inFrames;
- outFrames -= inFrames;
+ e0 = enabledTracks;
+ while (e0) {
+ // process by group of tracks with same output buffer to
+ // optimize cache use
+ uint32_t e1 = e0, e2 = e0;
+ int j = 31 - __builtin_clz(e1);
+ track_t& t1 = state->tracks[j];
+ e2 &= ~(1<<j);
+ while (e2) {
+ j = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<j);
+ track_t& t2 = state->tracks[j];
+ if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ e1 &= ~(1<<j);
+ }
+ }
+ e0 &= ~(e1);
+ // this assumes output 16 bits stereo, no resampling
+ int32_t *out = t1.mainBuffer;
+ size_t numFrames = 0;
+ do {
+ memset(outTemp, 0, sizeof(outTemp));
+ e2 = e1;
+ while (e2) {
+ const int i = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ size_t outFrames = BLOCKSIZE;
+ int32_t *aux = NULL;
+ if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ aux = t.auxBuffer + numFrames;
}
- if (t.frameCount == 0 && outFrames) {
- t.bufferProvider->releaseBuffer(&t.buffer);
- t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames);
- t.bufferProvider->getNextBuffer(&t.buffer);
- t.in = t.buffer.raw;
- if (t.in == NULL) {
- enabledTracks &= ~(1<<i);
- break;
+ while (outFrames) {
+ size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
+ if (inFrames) {
+ (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux);
+ t.frameCount -= inFrames;
+ outFrames -= inFrames;
+ if UNLIKELY(aux != NULL) {
+ aux += inFrames;
+ }
}
- t.frameCount = t.buffer.frameCount;
- }
+ if (t.frameCount == 0 && outFrames) {
+ t.bufferProvider->releaseBuffer(&t.buffer);
+ t.buffer.frameCount = (state->frameCount - numFrames) - (BLOCKSIZE - outFrames);
+ t.bufferProvider->getNextBuffer(&t.buffer);
+ t.in = t.buffer.raw;
+ if (t.in == NULL) {
+ enabledTracks &= ~(1<<i);
+ e1 &= ~(1<<i);
+ break;
+ }
+ t.frameCount = t.buffer.frameCount;
+ }
+ }
}
- }
-
- ditherAndClamp(out, outTemp, BLOCKSIZE);
- out += BLOCKSIZE;
- numFrames -= BLOCKSIZE;
- } while (numFrames);
-
+ ditherAndClamp(out, outTemp, BLOCKSIZE);
+ out += BLOCKSIZE;
+ numFrames += BLOCKSIZE;
+ } while (numFrames < state->frameCount);
+ }
// release each track's buffer
- en = enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
+ e0 = enabledTracks;
+ while (e0) {
+ const int i = 31 - __builtin_clz(e0);
+ e0 &= ~(1<<i);
track_t& t = state->tracks[i];
t.bufferProvider->releaseBuffer(&t.buffer);
}
}
-// generic code with resampling
-void AudioMixer::process__genericResampling(state_t* state, void* output)
+
+ // generic code with resampling
+void AudioMixer::process__genericResampling(state_t* state)
{
int32_t* const outTemp = state->outputTemp;
const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
memset(outTemp, 0, size);
- int32_t* out = static_cast<int32_t*>(output);
size_t numFrames = state->frameCount;
- uint32_t en = state->enabledTracks;
- while (en) {
- const int i = 31 - __builtin_clz(en);
- en &= ~(1<<i);
- track_t& t = state->tracks[i];
+ uint32_t e0 = state->enabledTracks;
+ while (e0) {
+ // process by group of tracks with same output buffer
+ // to optimize cache use
+ uint32_t e1 = e0, e2 = e0;
+ int j = 31 - __builtin_clz(e1);
+ track_t& t1 = state->tracks[j];
+ e2 &= ~(1<<j);
+ while (e2) {
+ j = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<j);
+ track_t& t2 = state->tracks[j];
+ if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
+ e1 &= ~(1<<j);
+ }
+ }
+ e0 &= ~(e1);
+ int32_t *out = t1.mainBuffer;
+ while (e1) {
+ const int i = 31 - __builtin_clz(e1);
+ e1 &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ int32_t *aux = NULL;
+ if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
+ aux = t.auxBuffer;
+ }
- // this is a little goofy, on the resampling case we don't
- // acquire/release the buffers because it's done by
- // the resampler.
- if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
- (t.hook)(&t, outTemp, numFrames, state->resampleTemp);
- } else {
+ // this is a little goofy, on the resampling case we don't
+ // acquire/release the buffers because it's done by
+ // the resampler.
+ if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+ (t.hook)(&t, outTemp, numFrames, state->resampleTemp, aux);
+ } else {
- size_t outFrames = numFrames;
-
- while (outFrames) {
- t.buffer.frameCount = outFrames;
- t.bufferProvider->getNextBuffer(&t.buffer);
- t.in = t.buffer.raw;
- // t.in == NULL can happen if the track was flushed just after having
- // been enabled for mixing.
- if (t.in == NULL) break;
-
- (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp);
- outFrames -= t.buffer.frameCount;
- t.bufferProvider->releaseBuffer(&t.buffer);
+ size_t outFrames = 0;
+
+ while (outFrames < numFrames) {
+ t.buffer.frameCount = numFrames - outFrames;
+ t.bufferProvider->getNextBuffer(&t.buffer);
+ t.in = t.buffer.raw;
+ // t.in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (t.in == NULL) break;
+
+ if UNLIKELY(aux != NULL) {
+ aux += outFrames;
+ }
+ (t.hook)(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux);
+ outFrames += t.buffer.frameCount;
+ t.bufferProvider->releaseBuffer(&t.buffer);
+ }
}
}
+ ditherAndClamp(out, outTemp, numFrames);
}
-
- ditherAndClamp(out, outTemp, numFrames);
}
// one track, 16 bits stereo without resampling is the most common case
-void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output)
+void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
{
const int i = 31 - __builtin_clz(state->enabledTracks);
const track_t& t = state->tracks[i];
AudioBufferProvider::Buffer& b(t.buffer);
-
- int32_t* out = static_cast<int32_t*>(output);
+
+ int32_t* out = t.mainBuffer;
size_t numFrames = state->frameCount;
-
+
const int16_t vl = t.volume[0];
const int16_t vr = t.volume[1];
const uint32_t vrl = t.volumeRL;
@@ -787,7 +1064,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void*
return;
}
size_t outFrames = b.frameCount;
-
+
if (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.
@@ -816,7 +1093,9 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void*
}
// 2 tracks is also a common case
-void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output)
+// NEVER used in current implementation of process__validate()
+// only use if the 2 tracks have the same output buffer
+void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
{
int i;
uint32_t en = state->enabledTracks;
@@ -829,24 +1108,25 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void
i = 31 - __builtin_clz(en);
const track_t& t1 = state->tracks[i];
AudioBufferProvider::Buffer& b1(t1.buffer);
-
+
int16_t const *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 vl1 = t1.volume[0];
const int16_t vr1 = t1.volume[1];
size_t frameCount1 = 0;
-
- int32_t* out = static_cast<int32_t*>(output);
+
+ //FIXME: only works if two tracks use same buffer
+ int32_t* out = t0.mainBuffer;
size_t numFrames = state->frameCount;
int16_t const *buff = NULL;
-
+
while (numFrames) {
-
+
if (frameCount0 == 0) {
b0.frameCount = numFrames;
t0.bufferProvider->getNextBuffer(&b0);
@@ -875,13 +1155,13 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void
}
frameCount1 = b1.frameCount;
}
-
+
size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1;
numFrames -= outFrames;
frameCount0 -= outFrames;
frameCount1 -= outFrames;
-
+
do {
int32_t l0 = *in0++;
int32_t r0 = *in0++;
@@ -896,17 +1176,17 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void
r = clamp16(r);
*out++ = (r<<16) | (l & 0xFFFF);
} while (--outFrames);
-
+
if (frameCount0 == 0) {
t0.bufferProvider->releaseBuffer(&b0);
}
if (frameCount1 == 0) {
t1.bufferProvider->releaseBuffer(&b1);
}
- }
-
+ }
+
if (buff != NULL) {
- delete [] buff;
+ delete [] buff;
}
}
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 15766cd..aee3e17 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -63,11 +63,14 @@ public:
// for target TRACK
CHANNEL_COUNT = 0x4000,
FORMAT = 0x4001,
+ MAIN_BUFFER = 0x4002,
+ AUX_BUFFER = 0x4003,
// for TARGET RESAMPLE
SAMPLE_RATE = 0x4100,
// for TARGET VOLUME (8 channels max)
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
+ AUXLEVEL = 0x4210,
};
@@ -78,10 +81,10 @@ public:
status_t disable(int name);
status_t setActiveTrack(int track);
- status_t setParameter(int target, int name, int value);
+ status_t setParameter(int target, int name, void *value);
status_t setBufferProvider(AudioBufferProvider* bufferProvider);
- void process(void* output);
+ void process();
uint32_t trackNames() const { return mTrackNames; }
@@ -94,6 +97,7 @@ private:
NEEDS_FORMAT__MASK = 0x000000F0,
NEEDS_MUTE__MASK = 0x00000100,
NEEDS_RESAMPLE__MASK = 0x00001000,
+ NEEDS_AUX__MASK = 0x00010000,
};
enum {
@@ -107,6 +111,9 @@ private:
NEEDS_RESAMPLE_DISABLED = 0x00000000,
NEEDS_RESAMPLE_ENABLED = 0x00001000,
+
+ NEEDS_AUX_DISABLED = 0x00000000,
+ NEEDS_AUX_ENABLED = 0x00010000,
};
static inline int32_t applyVolume(int32_t in, int32_t v) {
@@ -115,9 +122,10 @@ private:
struct state_t;
+ struct track_t;
- typedef void (*mix_t)(state_t* state, void* output);
-
+ typedef void (*mix_t)(state_t* state);
+ typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
static const int BLOCKSIZE = 16; // 4 cache lines
struct track_t {
@@ -131,6 +139,9 @@ private:
int32_t prevVolume[2];
int32_t volumeInc[2];
+ int32_t auxLevel;
+ int32_t auxInc;
+ int32_t prevAuxLevel;
uint16_t frameCount;
@@ -142,15 +153,17 @@ private:
AudioBufferProvider* bufferProvider;
mutable AudioBufferProvider::Buffer buffer;
- void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp);
+ hook_t hook;
void const* in; // current location in buffer
AudioResampler* resampler;
uint32_t sampleRate;
+ int32_t* mainBuffer;
+ int32_t* auxBuffer;
bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
bool doesResample() const;
- void adjustVolumeRamp();
+ void adjustVolumeRamp(bool aux);
};
// pad to 32-bytes to fill cache line
@@ -173,18 +186,19 @@ private:
void invalidateState(uint32_t mask);
- static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
- static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
- static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp);
- static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
- static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
-
- static void process__validate(state_t* state, void* output);
- static void process__nop(state_t* state, void* output);
- static void process__genericNoResampling(state_t* state, void* output);
- static void process__genericResampling(state_t* state, void* output);
- static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output);
- static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output);
+ static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
+ static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
+
+ static void process__validate(state_t* state);
+ static void process__nop(state_t* state);
+ static void process__genericNoResampling(state_t* state);
+ static void process__genericResampling(state_t* state);
+ static void process__OneTrack16BitsStereoNoResampling(state_t* state);
+ static void process__TwoTracks16BitsStereoNoResampling(state_t* state);
};
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index c8b3f48..425ca31 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -133,7 +133,7 @@ status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_dev
// request routing change if necessary
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
- checkOutputForAllStrategies(newDevice);
+ checkOutputForAllStrategies();
// A2DP outputs must be closed after checkOutputForAllStrategies() is executed
if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
closeA2dpOutputs();
@@ -274,7 +274,7 @@ void AudioPolicyManagerBase::setPhoneState(int state)
// check for device and output changes triggered by new phone state
newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
- checkOutputForAllStrategies(newDevice);
+ checkOutputForAllStrategies();
// suspend A2DP output if a SCO device is present.
if (mA2dpOutput != 0 && mScoDeviceAddress != "") {
if (oldState == AudioSystem::MODE_NORMAL) {
@@ -386,13 +386,28 @@ void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSyst
// check for device and output changes triggered by new phone state
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
- checkOutputForAllStrategies(newDevice);
+ checkOutputForAllStrategies();
#endif
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
if (forceVolumeReeval) {
applyStreamVolumes(mHardwareOutput, newDevice);
}
+
+ audio_io_handle_t activeInput = getActiveInput();
+ if (activeInput != 0) {
+ AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
+ newDevice = getDeviceForInputSource(inputDesc->mInputSource);
+ if (newDevice != inputDesc->mDevice) {
+ LOGV("setForceUse() changing device from %x to %x for input %d",
+ inputDesc->mDevice, newDevice, activeInput);
+ inputDesc->mDevice = newDevice;
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
+ mpClientInterface->setParameters(activeInput, param.toString());
+ }
+ }
+
}
AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage)
@@ -538,9 +553,11 @@ audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type str
return output;
}
-status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
+ AudioSystem::stream_type stream,
+ int session)
{
- LOGV("startOutput() output %d, stream %d", output, stream);
+ LOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
ssize_t index = mOutputs.indexOfKey(output);
if (index < 0) {
LOGW("startOutput() unknow output %d", output);
@@ -574,9 +591,11 @@ status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, AudioSyst
return NO_ERROR;
}
-status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output,
+ AudioSystem::stream_type stream,
+ int session)
{
- LOGV("stopOutput() output %d, stream %d", output, stream);
+ LOGV("stopOutput() output %d, stream %d, session %d", output, stream, session);
ssize_t index = mOutputs.indexOfKey(output);
if (index < 0) {
LOGW("stopOutput() unknow output %d", output);
@@ -602,8 +621,12 @@ status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, AudioSyste
setOutputDevice(output, getNewDevice(output));
#ifdef WITH_A2DP
- if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
- setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput, mOutputs.valueFor(mHardwareOutput)->mLatency*2);
+ if (mA2dpOutput != 0 && !a2dpUsedForSonification() &&
+ strategy == STRATEGY_SONIFICATION) {
+ setStrategyMute(STRATEGY_MEDIA,
+ false,
+ mA2dpOutput,
+ mOutputs.valueFor(mHardwareOutput)->mLatency*2);
}
#endif
if (output != mHardwareOutput) {
@@ -826,6 +849,85 @@ status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type s
return NO_ERROR;
}
+audio_io_handle_t AudioPolicyManagerBase::getOutputForEffect(effect_descriptor_t *desc)
+{
+ LOGV("getOutputForEffect()");
+ // apply simple rule where global effects are attached to the same output as MUSIC streams
+ return getOutput(AudioSystem::MUSIC);
+}
+
+status_t AudioPolicyManagerBase::registerEffect(effect_descriptor_t *desc,
+ audio_io_handle_t output,
+ uint32_t strategy,
+ int session,
+ int id)
+{
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("registerEffect() unknown output %d", output);
+ return INVALID_OPERATION;
+ }
+
+ if (mTotalEffectsCpuLoad + desc->cpuLoad > getMaxEffectsCpuLoad()) {
+ LOGW("registerEffect() CPU Load limit exceeded for Fx %s, CPU %f MIPS",
+ desc->name, (float)desc->cpuLoad/10);
+ return INVALID_OPERATION;
+ }
+ if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) {
+ LOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB",
+ desc->name, desc->memoryUsage);
+ return INVALID_OPERATION;
+ }
+ mTotalEffectsCpuLoad += desc->cpuLoad;
+ mTotalEffectsMemory += desc->memoryUsage;
+ LOGV("registerEffect() effect %s, output %d, strategy %d session %d id %d",
+ desc->name, output, strategy, session, id);
+
+ LOGV("registerEffect() CPU %d, memory %d", desc->cpuLoad, desc->memoryUsage);
+ LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+
+ EffectDescriptor *pDesc = new EffectDescriptor();
+ memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t));
+ pDesc->mOutput = output;
+ pDesc->mStrategy = (routing_strategy)strategy;
+ pDesc->mSession = session;
+ mEffects.add(id, pDesc);
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::unregisterEffect(int id)
+{
+ ssize_t index = mEffects.indexOfKey(id);
+ if (index < 0) {
+ LOGW("unregisterEffect() unknown effect ID %d", id);
+ return INVALID_OPERATION;
+ }
+
+ EffectDescriptor *pDesc = mEffects.valueAt(index);
+
+ if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) {
+ LOGW("unregisterEffect() CPU load %d too high for total %d",
+ pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad);
+ pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad;
+ }
+ mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad;
+ if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) {
+ LOGW("unregisterEffect() memory %d too big for total %d",
+ pDesc->mDesc.memoryUsage, mTotalEffectsMemory);
+ pDesc->mDesc.memoryUsage = mTotalEffectsMemory;
+ }
+ mTotalEffectsMemory -= pDesc->mDesc.memoryUsage;
+ LOGV("unregisterEffect() effect %s, ID %d, CPU %d, memory %d",
+ pDesc->mDesc.name, id, pDesc->mDesc.cpuLoad, pDesc->mDesc.memoryUsage);
+ LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+
+ mEffects.removeItem(id);
+ delete pDesc;
+
+ return NO_ERROR;
+}
+
status_t AudioPolicyManagerBase::dump(int fd)
{
const size_t SIZE = 256;
@@ -890,6 +992,19 @@ status_t AudioPolicyManagerBase::dump(int fd)
write(fd, buffer, strlen(buffer));
}
+ snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n",
+ (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory);
+ write(fd, buffer, strlen(buffer));
+
+ snprintf(buffer, SIZE, "Registered effects:\n");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i));
+ write(fd, buffer, strlen(buffer));
+ mEffects.valueAt(i)->dump(fd);
+ }
+
+
return NO_ERROR;
}
@@ -902,7 +1017,8 @@ AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clien
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
- mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false)
+ mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0),
+ mLimitRingtoneVolume(false), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
{
mpClientInterface = clientInterface;
@@ -938,6 +1054,7 @@ AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clien
} else {
addOutput(mHardwareOutput, outputDesc);
setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
+ //TODO: configure audio effect output stage here
}
updateDeviceForStrategy();
@@ -1152,6 +1269,9 @@ status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices
if (mA2dpOutput) {
// add A2DP output descriptor
addOutput(mA2dpOutput, outputDesc);
+
+ //TODO: configure audio effect output stage here
+
// set initial stream volume for A2DP device
applyStreamVolumes(mA2dpOutput, device);
if (a2dpUsedForSonification()) {
@@ -1249,6 +1369,17 @@ void AudioPolicyManagerBase::closeA2dpOutputs()
LOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
if (mDuplicatedOutput != 0) {
+ AudioOutputDescriptor *dupOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+ // As all active tracks on duplicated output will be deleted,
+ // and as they were also referenced on hardware output, the reference
+ // count for their stream type must be adjusted accordingly on
+ // hardware output.
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ int refCount = dupOutputDesc->mRefCount[i];
+ hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
+ }
+
mpClientInterface->closeOutput(mDuplicatedOutput);
delete mOutputs.valueFor(mDuplicatedOutput);
mOutputs.removeItem(mDuplicatedOutput);
@@ -1258,6 +1389,7 @@ void AudioPolicyManagerBase::closeA2dpOutputs()
AudioParameter param;
param.add(String8("closing"), String8("true"));
mpClientInterface->setParameters(mA2dpOutput, param.toString());
+
mpClientInterface->closeOutput(mA2dpOutput);
delete mOutputs.valueFor(mA2dpOutput);
mOutputs.removeItem(mA2dpOutput);
@@ -1265,78 +1397,65 @@ void AudioPolicyManagerBase::closeA2dpOutputs()
}
}
-void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, uint32_t &newDevice)
+void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy)
{
uint32_t prevDevice = getDeviceForStrategy(strategy);
uint32_t curDevice = getDeviceForStrategy(strategy, false);
bool a2dpWasUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(prevDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
bool a2dpIsUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(curDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
- AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
- AudioOutputDescriptor *a2dpOutputDesc;
+ audio_io_handle_t srcOutput = 0;
+ audio_io_handle_t dstOutput = 0;
if (a2dpWasUsed && !a2dpIsUsed) {
bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2);
-
+ dstOutput = mHardwareOutput;
if (dupUsed) {
- LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy);
- a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
+ LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy);
+ srcOutput = mDuplicatedOutput;
} else {
- LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy);
- a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput);
- }
-
- for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
- if (getStrategy((AudioSystem::stream_type)i) == strategy) {
- mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput);
- int refCount = a2dpOutputDesc->mRefCount[i];
- // in the case of duplicated output, the ref count is first incremented
- // and then decremented on hardware output tus keeping its value
- hwOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount);
- a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
- }
- }
- // do not change newDevice if it was already set before this call by a previous call to
- // getNewDevice() or checkOutputForStrategy() for a strategy with higher priority
- if (newDevice == 0 && hwOutputDesc->isUsedByStrategy(strategy)) {
- newDevice = getDeviceForStrategy(strategy, false);
+ LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy);
+ srcOutput = mA2dpOutput;
}
}
if (a2dpIsUsed && !a2dpWasUsed) {
bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2);
- audio_io_handle_t a2dpOutput;
-
+ srcOutput = mHardwareOutput;
if (dupUsed) {
- LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy);
- a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
- a2dpOutput = mDuplicatedOutput;
+ LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy);
+ dstOutput = mDuplicatedOutput;
} else {
- LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy);
- a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput);
- a2dpOutput = mA2dpOutput;
+ LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy);
+ dstOutput = mA2dpOutput;
}
+ }
+ if (srcOutput != 0 && dstOutput != 0) {
+ // Move effects associated to this strategy from previous output to new output
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ EffectDescriptor *desc = mEffects.valueAt(i);
+ if (desc->mSession != AudioSystem::SESSION_OUTPUT_STAGE &&
+ desc->mStrategy == strategy &&
+ desc->mOutput == srcOutput) {
+ LOGV("checkOutputForStrategy() moving effect %d to output %d", mEffects.keyAt(i), dstOutput);
+ mpClientInterface->moveEffects(desc->mSession, srcOutput, dstOutput);
+ desc->mOutput = dstOutput;
+ }
+ }
+ // Move tracks associated to this strategy from previous output to new output
for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
if (getStrategy((AudioSystem::stream_type)i) == strategy) {
- mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, a2dpOutput);
- int refCount = hwOutputDesc->mRefCount[i];
- // in the case of duplicated output, the ref count is first incremented
- // and then decremented on hardware output tus keeping its value
- a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount);
- hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, dstOutput);
}
}
}
}
-void AudioPolicyManagerBase::checkOutputForAllStrategies(uint32_t &newDevice)
+void AudioPolicyManagerBase::checkOutputForAllStrategies()
{
- // Check strategies in order of priority so that once newDevice is set
- // for a given strategy it is not modified by subsequent calls to
- // checkOutputForStrategy()
- checkOutputForStrategy(STRATEGY_PHONE, newDevice);
- checkOutputForStrategy(STRATEGY_SONIFICATION, newDevice);
- checkOutputForStrategy(STRATEGY_MEDIA, newDevice);
- checkOutputForStrategy(STRATEGY_DTMF, newDevice);
+ checkOutputForStrategy(STRATEGY_PHONE);
+ checkOutputForStrategy(STRATEGY_SONIFICATION);
+ checkOutputForStrategy(STRATEGY_MEDIA);
+ checkOutputForStrategy(STRATEGY_DTMF);
}
#endif
@@ -1370,8 +1489,12 @@ uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fro
return device;
}
-AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(AudioSystem::stream_type stream)
-{
+uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type stream) {
+ return (uint32_t)getStrategy(stream);
+}
+
+AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(
+ AudioSystem::stream_type stream) {
// stream to strategy mapping
switch (stream) {
case AudioSystem::VOICE_CALL:
@@ -1835,6 +1958,16 @@ bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream,
(format !=0 && !AudioSystem::isLinearPCM(format)));
}
+uint32_t AudioPolicyManagerBase::getMaxEffectsCpuLoad()
+{
+ return MAX_EFFECTS_CPU_LOAD;
+}
+
+uint32_t AudioPolicyManagerBase::getMaxEffectsMemory()
+{
+ return MAX_EFFECTS_MEMORY;
+}
+
// --- AudioOutputDescriptor class implementation
AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()
@@ -1968,5 +2101,27 @@ void AudioPolicyManagerBase::StreamDescriptor::dump(char* buffer, size_t size)
mCanBeMuted);
}
+// --- EffectDescriptor class implementation
+
+status_t AudioPolicyManagerBase::EffectDescriptor::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, " Output: %d\n", mOutput);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Session: %d\n", mSession);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Name: %s\n", mDesc.name);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+
}; // namespace android
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index bb3905c..f24e08e 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -119,7 +119,8 @@ status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices
if (!AudioSystem::isOutputDevice(device) && !AudioSystem::isInputDevice(device)) {
return BAD_VALUE;
}
- if (state != AudioSystem::DEVICE_STATE_AVAILABLE && state != AudioSystem::DEVICE_STATE_UNAVAILABLE) {
+ if (state != AudioSystem::DEVICE_STATE_AVAILABLE &&
+ state != AudioSystem::DEVICE_STATE_UNAVAILABLE) {
return BAD_VALUE;
}
@@ -128,8 +129,9 @@ status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices
return mpPolicyManager->setDeviceConnectionState(device, state, device_address);
}
-AudioSystem::device_connection_state AudioPolicyService::getDeviceConnectionState(AudioSystem::audio_devices device,
- const char *device_address)
+AudioSystem::device_connection_state AudioPolicyService::getDeviceConnectionState(
+ AudioSystem::audio_devices device,
+ const char *device_address)
{
if (mpPolicyManager == NULL) {
return AudioSystem::DEVICE_STATE_UNAVAILABLE;
@@ -175,7 +177,8 @@ status_t AudioPolicyService::setRingerMode(uint32_t mode, uint32_t mask)
return NO_ERROR;
}
-status_t AudioPolicyService::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+status_t AudioPolicyService::setForceUse(AudioSystem::force_use usage,
+ AudioSystem::forced_config config)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
@@ -223,24 +226,28 @@ audio_io_handle_t AudioPolicyService::getOutput(AudioSystem::stream_type stream,
return mpPolicyManager->getOutput(stream, samplingRate, format, channels, flags);
}
-status_t AudioPolicyService::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+status_t AudioPolicyService::startOutput(audio_io_handle_t output,
+ AudioSystem::stream_type stream,
+ int session)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
}
LOGV("startOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->startOutput(output, stream);
+ return mpPolicyManager->startOutput(output, stream, session);
}
-status_t AudioPolicyService::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
+ AudioSystem::stream_type stream,
+ int session)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
}
LOGV("stopOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->stopOutput(output, stream);
+ return mpPolicyManager->stopOutput(output, stream, session);
}
void AudioPolicyService::releaseOutput(audio_io_handle_t output)
@@ -339,8 +346,46 @@ status_t AudioPolicyService::getStreamVolumeIndex(AudioSystem::stream_type strea
return mpPolicyManager->getStreamVolumeIndex(stream, index);
}
+uint32_t AudioPolicyService::getStrategyForStream(AudioSystem::stream_type stream)
+{
+ if (mpPolicyManager == NULL) {
+ return 0;
+ }
+ return mpPolicyManager->getStrategyForStream(stream);
+}
+
+audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *desc)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->getOutputForEffect(desc);
+}
+
+status_t AudioPolicyService::registerEffect(effect_descriptor_t *desc,
+ audio_io_handle_t output,
+ uint32_t strategy,
+ int session,
+ int id)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ return mpPolicyManager->registerEffect(desc, output, strategy, session, id);
+}
+
+status_t AudioPolicyService::unregisterEffect(int id)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ return mpPolicyManager->unregisterEffect(id);
+}
+
void AudioPolicyService::binderDied(const wp<IBinder>& who) {
- LOGW("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid());
+ LOGW("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(),
+ IPCThreadState::self()->getCallingPid());
}
static bool tryLock(Mutex& mutex)
@@ -447,10 +492,16 @@ audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,
return 0;
}
- return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, pLatencyMs, flags);
+ return af->openOutput(pDevices,
+ pSamplingRate,
+ (uint32_t *)pFormat,
+ pChannels,
+ pLatencyMs,
+ flags);
}
-audio_io_handle_t AudioPolicyService::openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2)
+audio_io_handle_t AudioPolicyService::openDuplicateOutput(audio_io_handle_t output1,
+ audio_io_handle_t output2)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -514,12 +565,16 @@ status_t AudioPolicyService::closeInput(audio_io_handle_t input)
return af->closeInput(input);
}
-status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs)
+status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream,
+ float volume,
+ audio_io_handle_t output,
+ int delayMs)
{
return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output, delayMs);
}
-status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output)
+status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream,
+ audio_io_handle_t output)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -527,8 +582,18 @@ status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream, au
return af->setStreamOutput(stream, output);
}
+status_t AudioPolicyService::moveEffects(int session, audio_io_handle_t srcOutput,
+ audio_io_handle_t dstOutput)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+
+ return af->moveEffects(session, (int)srcOutput, (int)dstOutput);
+}
-void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs)
+void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
+ const String8& keyValuePairs,
+ int delayMs)
{
mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs);
}
@@ -539,7 +604,8 @@ String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const Stri
return result;
}
-status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream)
+status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone,
+ AudioSystem::stream_type stream)
{
mTonePlaybackThread->startToneCommand(tone, stream);
return NO_ERROR;
@@ -623,8 +689,11 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
}break;
case SET_VOLUME: {
VolumeData *data = (VolumeData *)command->mParam;
- LOGV("AudioCommandThread() processing set volume stream %d, volume %f, output %d", data->mStream, data->mVolume, data->mIO);
- command->mStatus = AudioSystem::setStreamVolume(data->mStream, data->mVolume, data->mIO);
+ LOGV("AudioCommandThread() processing set volume stream %d, \
+ volume %f, output %d", data->mStream, data->mVolume, data->mIO);
+ command->mStatus = AudioSystem::setStreamVolume(data->mStream,
+ data->mVolume,
+ data->mIO);
if (command->mWaitStatus) {
command->mCond.signal();
mWaitWorkCV.wait(mLock);
@@ -633,7 +702,8 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
}break;
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
- LOGV("AudioCommandThread() processing set parameters string %s, io %d", data->mKeyValuePairs.string(), data->mIO);
+ LOGV("AudioCommandThread() processing set parameters string %s, io %d",
+ data->mKeyValuePairs.string(), data->mIO);
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
if (command->mWaitStatus) {
command->mCond.signal();
@@ -643,7 +713,8 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
}break;
case SET_VOICE_VOLUME: {
VoiceVolumeData *data = (VoiceVolumeData *)command->mParam;
- LOGV("AudioCommandThread() processing set voice volume volume %f", data->mVolume);
+ LOGV("AudioCommandThread() processing set voice volume volume %f",
+ data->mVolume);
command->mStatus = AudioSystem::setVoiceVolume(data->mVolume);
if (command->mWaitStatus) {
command->mCond.signal();
@@ -734,7 +805,10 @@ void AudioPolicyService::AudioCommandThread::stopToneCommand()
mWaitWorkCV.signal();
}
-status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float volume, int output, int delayMs)
+status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream,
+ float volume,
+ int output,
+ int delayMs)
{
status_t status = NO_ERROR;
@@ -752,7 +826,8 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float
}
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
- LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", stream, volume, output);
+ LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d",
+ stream, volume, output);
mWaitWorkCV.signal();
if (command->mWaitStatus) {
command->mCond.wait(mLock);
@@ -762,7 +837,9 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float
return status;
}
-status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs)
+status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
+ const String8& keyValuePairs,
+ int delayMs)
{
status_t status = NO_ERROR;
@@ -779,7 +856,8 @@ status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
}
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
- LOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", keyValuePairs.string(), ioHandle, delayMs);
+ LOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d",
+ keyValuePairs.string(), ioHandle, delayMs);
mWaitWorkCV.signal();
if (command->mWaitStatus) {
command->mCond.wait(mLock);
@@ -840,7 +918,8 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
ParametersData *data = (ParametersData *)command->mParam;
ParametersData *data2 = (ParametersData *)command2->mParam;
if (data->mIO != data2->mIO) break;
- LOGV("Comparing parameter command %s to new command %s", data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
+ LOGV("Comparing parameter command %s to new command %s",
+ data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
AudioParameter param = AudioParameter(data->mKeyValuePairs);
AudioParameter param2 = AudioParameter(data2->mKeyValuePairs);
for (size_t j = 0; j < param.size(); j++) {
@@ -872,7 +951,8 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
VolumeData *data2 = (VolumeData *)command2->mParam;
if (data->mIO != data2->mIO) break;
if (data->mStream != data2->mStream) break;
- LOGV("Filtering out volume command on output %d for stream %d", data->mIO, data->mStream);
+ LOGV("Filtering out volume command on output %d for stream %d",
+ data->mIO, data->mStream);
removedCommands.add(command2);
} break;
case START_TONE:
@@ -896,7 +976,8 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
removedCommands.clear();
// insert command at the right place according to its time stamp
- LOGV("inserting command: %d at index %d, num commands %d", command->mCommand, (int)i+1, mAudioCommands.size());
+ LOGV("inserting command: %d at index %d, num commands %d",
+ command->mCommand, (int)i+1, mAudioCommands.size());
mAudioCommands.insertAt(command, i + 1);
}
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index a13d0bd..558f455 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -28,7 +28,8 @@ class String8;
// ----------------------------------------------------------------------------
-class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface, public IBinder::DeathRecipient
+class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface,
+ public IBinder::DeathRecipient
{
public:
@@ -43,8 +44,9 @@ public:
virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
AudioSystem::device_connection_state state,
const char *device_address);
- virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
- const char *device_address);
+ virtual AudioSystem::device_connection_state getDeviceConnectionState(
+ AudioSystem::audio_devices device,
+ const char *device_address);
virtual status_t setPhoneState(int state);
virtual status_t setRingerMode(uint32_t mode, uint32_t mask);
virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
@@ -53,15 +55,21 @@ public:
uint32_t samplingRate = 0,
uint32_t format = AudioSystem::FORMAT_DEFAULT,
uint32_t channels = 0,
- AudioSystem::output_flags flags = AudioSystem::OUTPUT_FLAG_INDIRECT);
- virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
- virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ AudioSystem::output_flags flags =
+ AudioSystem::OUTPUT_FLAG_INDIRECT);
+ virtual status_t startOutput(audio_io_handle_t output,
+ AudioSystem::stream_type stream,
+ int session = 0);
+ virtual status_t stopOutput(audio_io_handle_t output,
+ AudioSystem::stream_type stream,
+ int session = 0);
virtual void releaseOutput(audio_io_handle_t output);
virtual audio_io_handle_t getInput(int inputSource,
uint32_t samplingRate = 0,
uint32_t format = AudioSystem::FORMAT_DEFAULT,
uint32_t channels = 0,
- AudioSystem::audio_in_acoustics acoustics = (AudioSystem::audio_in_acoustics)0);
+ AudioSystem::audio_in_acoustics acoustics =
+ (AudioSystem::audio_in_acoustics)0);
virtual status_t startInput(audio_io_handle_t input);
virtual status_t stopInput(audio_io_handle_t input);
virtual void releaseInput(audio_io_handle_t input);
@@ -71,6 +79,16 @@ public:
virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);
+ virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream);
+
+ virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc);
+ virtual status_t registerEffect(effect_descriptor_t *desc,
+ audio_io_handle_t output,
+ uint32_t strategy,
+ int session,
+ int id);
+ virtual status_t unregisterEffect(int id);
+
virtual status_t onTransact(
uint32_t code,
const Parcel& data,
@@ -89,7 +107,8 @@ public:
uint32_t *pChannels,
uint32_t *pLatencyMs,
AudioSystem::output_flags flags);
- virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2);
+ virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
+ audio_io_handle_t output2);
virtual status_t closeOutput(audio_io_handle_t output);
virtual status_t suspendOutput(audio_io_handle_t output);
virtual status_t restoreOutput(audio_io_handle_t output);
@@ -99,13 +118,21 @@ public:
uint32_t *pChannels,
uint32_t acoustics);
virtual status_t closeInput(audio_io_handle_t input);
- virtual status_t setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs = 0);
+ virtual status_t setStreamVolume(AudioSystem::stream_type stream,
+ float volume,
+ audio_io_handle_t output,
+ int delayMs = 0);
virtual status_t setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output);
- virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0);
+ virtual void setParameters(audio_io_handle_t ioHandle,
+ const String8& keyValuePairs,
+ int delayMs = 0);
virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys);
virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream);
virtual status_t stopTone();
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
+ virtual status_t moveEffects(int session,
+ audio_io_handle_t srcOutput,
+ audio_io_handle_t dstOutput);
private:
AudioPolicyService();
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index df5c166..87975af 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -1,15 +1,14 @@
LOCAL_PATH:= $(call my-dir)
-#
-# Set USE_CAMERA_STUB for non-emulator and non-simulator builds, if you want
-# the camera service to use the fake camera. For emulator or simulator builds,
-# we always use the fake camera.
+# Set USE_CAMERA_STUB if you don't want to use the hardware camera.
-ifeq ($(USE_CAMERA_STUB),)
-USE_CAMERA_STUB:=false
+# force these builds to use camera stub only
ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
-USE_CAMERA_STUB:=true
-endif #libcamerastub
+ USE_CAMERA_STUB:=true
+endif
+
+ifeq ($(USE_CAMERA_STUB),)
+ USE_CAMERA_STUB:=false
endif
ifeq ($(USE_CAMERA_STUB),true)
@@ -54,18 +53,14 @@ LOCAL_SHARED_LIBRARIES:= \
LOCAL_MODULE:= libcameraservice
-LOCAL_CFLAGS += -DLOG_TAG=\"CameraService\"
-
ifeq ($(TARGET_SIMULATOR),true)
LOCAL_CFLAGS += -DSINGLE_PROCESS
endif
ifeq ($(USE_CAMERA_STUB), true)
LOCAL_STATIC_LIBRARIES += libcamerastub
-LOCAL_CFLAGS += -include CameraHardwareStub.h
else
LOCAL_SHARED_LIBRARIES += libcamera
endif
include $(BUILD_SHARED_LIBRARY)
-
diff --git a/services/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp
index 8b66389..b3e0ee6 100644
--- a/services/camera/libcameraservice/CameraHardwareStub.cpp
+++ b/services/camera/libcameraservice/CameraHardwareStub.cpp
@@ -47,14 +47,14 @@ void CameraHardwareStub::initDefaultParameters()
{
CameraParameters p;
- p.set("preview-size-values","320x240");
+ p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240");
p.setPreviewSize(320, 240);
p.setPreviewFrameRate(15);
- p.setPreviewFormat("yuv422sp");
+ p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
- p.set("picture-size-values", "320x240");
+ p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240");
p.setPictureSize(320, 240);
- p.setPictureFormat("jpeg");
+ p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);
if (setParameters(p) != NO_ERROR) {
LOGE("Failed to set default parameters?!");
@@ -66,14 +66,14 @@ void CameraHardwareStub::initHeapLocked()
// Create raw heap.
int picture_width, picture_height;
mParameters.getPictureSize(&picture_width, &picture_height);
- mRawHeap = new MemoryHeapBase(picture_width * 2 * picture_height);
+ mRawHeap = new MemoryHeapBase(picture_width * picture_height * 3 / 2);
int preview_width, preview_height;
mParameters.getPreviewSize(&preview_width, &preview_height);
LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height);
- // Note that we enforce yuv422 in setParameters().
- int how_big = preview_width * preview_height * 2;
+ // Note that we enforce yuv420sp in setParameters().
+ int how_big = preview_width * preview_height * 3 / 2;
// If we are being reinitialized to the same size as before, no
// work needs to be done.
@@ -99,7 +99,6 @@ CameraHardwareStub::~CameraHardwareStub()
{
delete mFakeCamera;
mFakeCamera = 0; // paranoia
- singleton.clear();
}
sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const
@@ -175,7 +174,7 @@ int CameraHardwareStub::previewThread()
// Fill the current frame with the fake camera.
uint8_t *frame = ((uint8_t *)base) + offset;
- fakeCamera->getNextFrameAsYuv422(frame);
+ fakeCamera->getNextFrameAsYuv420(frame);
//LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame);
@@ -288,9 +287,9 @@ int CameraHardwareStub::pictureThread()
// In the meantime just make another fake camera picture.
int w, h;
mParameters.getPictureSize(&w, &h);
- sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h);
+ sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * h * 3 / 2);
FakeCamera cam(w, h);
- cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base());
+ cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base());
mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie);
}
@@ -307,7 +306,7 @@ status_t CameraHardwareStub::takePicture()
{
stopPreview();
if (createThread(beginPictureThread, this) == false)
- return -1;
+ return UNKNOWN_ERROR;
return NO_ERROR;
}
@@ -339,12 +338,14 @@ status_t CameraHardwareStub::setParameters(const CameraParameters& params)
Mutex::Autolock lock(mLock);
// XXX verify params
- if (strcmp(params.getPreviewFormat(), "yuv422sp") != 0) {
- LOGE("Only yuv422sp preview is supported");
+ if (strcmp(params.getPreviewFormat(),
+ CameraParameters::PIXEL_FORMAT_YUV420SP) != 0) {
+ LOGE("Only yuv420sp preview is supported");
return -1;
}
- if (strcmp(params.getPictureFormat(), "jpeg") != 0) {
+ if (strcmp(params.getPictureFormat(),
+ CameraParameters::PIXEL_FORMAT_JPEG) != 0) {
LOGE("Only jpeg still pictures are supported");
return -1;
}
@@ -379,22 +380,29 @@ void CameraHardwareStub::release()
{
}
-wp<CameraHardwareInterface> CameraHardwareStub::singleton;
-
sp<CameraHardwareInterface> CameraHardwareStub::createInstance()
{
- if (singleton != 0) {
- sp<CameraHardwareInterface> hardware = singleton.promote();
- if (hardware != 0) {
- return hardware;
- }
+ return new CameraHardwareStub();
+}
+
+static CameraInfo sCameraInfo[] = {
+ {
+ CAMERA_FACING_BACK,
+ 90, /* orientation */
}
- sp<CameraHardwareInterface> hardware(new CameraHardwareStub());
- singleton = hardware;
- return hardware;
+};
+
+extern "C" int HAL_getNumberOfCameras()
+{
+ return sizeof(sCameraInfo) / sizeof(sCameraInfo[0]);
+}
+
+extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo)
+{
+ memcpy(cameraInfo, &sCameraInfo[cameraId], sizeof(CameraInfo));
}
-extern "C" sp<CameraHardwareInterface> openCameraHardware()
+extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId)
{
return CameraHardwareStub::createInstance();
}
diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h
index 957813a4..d3427ba 100644
--- a/services/camera/libcameraservice/CameraHardwareStub.h
+++ b/services/camera/libcameraservice/CameraHardwareStub.h
@@ -67,8 +67,6 @@ private:
CameraHardwareStub();
virtual ~CameraHardwareStub();
- static wp<CameraHardwareInterface> singleton;
-
static const int kBufferCount = 4;
class PreviewThread : public Thread {
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 5a55704..ea2c5d4 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -16,383 +16,435 @@
*/
#define LOG_TAG "CameraService"
-#include <utils/Log.h>
-#include <binder/IServiceManager.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <pthread.h>
+
#include <binder/IPCThreadState.h>
-#include <utils/String16.h>
-#include <utils/Errors.h>
+#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
-#include <camera/ICameraService.h>
+#include <cutils/atomic.h>
+#include <hardware/hardware.h>
+#include <media/AudioSystem.h>
+#include <media/mediaplayer.h>
#include <surfaceflinger/ISurface.h>
#include <ui/Overlay.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/String16.h>
-#include <hardware/hardware.h>
-
-#include <media/mediaplayer.h>
-#include <media/AudioSystem.h>
#include "CameraService.h"
-#include <cutils/atomic.h>
-
namespace android {
-extern "C" {
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <signal.h>
-}
-
-// When you enable this, as well as DEBUG_REFS=1 and
-// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp, this will track all
-// references to the CameraService::Client in order to catch the case where the
-// client is being destroyed while a callback from the CameraHardwareInterface
-// is outstanding. This is a serious bug because if we make another call into
-// CameraHardwreInterface that itself triggers a callback, we will deadlock.
-
-#define DEBUG_CLIENT_REFERENCES 0
+// ----------------------------------------------------------------------------
+// Logging support -- this is for debugging only
+// Use "adb shell dumpsys media.camera -v 1" to change it.
+static volatile int32_t gLogLevel = 0;
-#define PICTURE_TIMEOUT seconds(5)
+#define LOG1(...) LOGD_IF(gLogLevel >= 1, __VA_ARGS__);
+#define LOG2(...) LOGD_IF(gLogLevel >= 2, __VA_ARGS__);
-#define DEBUG_DUMP_PREVIEW_FRAME_TO_FILE 0 /* n-th frame to write */
-#define DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE 0
-#define DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE 0
-#define DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE 0
+static void setLogLevel(int level) {
+ android_atomic_write(level, &gLogLevel);
+}
-#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
-static int debug_frame_cnt;
-#endif
+// ----------------------------------------------------------------------------
static int getCallingPid() {
return IPCThreadState::self()->getCallingPid();
}
-// ----------------------------------------------------------------------------
-
-void CameraService::instantiate() {
- defaultServiceManager()->addService(
- String16("media.camera"), new CameraService());
+static int getCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
}
// ----------------------------------------------------------------------------
-CameraService::CameraService() :
- BnCameraService()
+// This is ugly and only safe if we never re-create the CameraService, but
+// should be ok for now.
+static CameraService *gCameraService;
+
+CameraService::CameraService()
+:mSoundRef(0)
{
- LOGI("CameraService started: pid=%d", getpid());
- mUsers = 0;
+ LOGI("CameraService started (pid=%d)", getpid());
+
+ mNumberOfCameras = HAL_getNumberOfCameras();
+ if (mNumberOfCameras > MAX_CAMERAS) {
+ LOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
+ mNumberOfCameras, MAX_CAMERAS);
+ mNumberOfCameras = MAX_CAMERAS;
+ }
+
+ for (int i = 0; i < mNumberOfCameras; i++) {
+ setCameraFree(i);
+ }
+
+ gCameraService = this;
}
-CameraService::~CameraService()
-{
- if (mClient != 0) {
- LOGE("mClient was still connected in destructor!");
+CameraService::~CameraService() {
+ for (int i = 0; i < mNumberOfCameras; i++) {
+ if (mBusy[i]) {
+ LOGE("camera %d is still in use in destructor!", i);
+ }
}
+
+ gCameraService = NULL;
}
-sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient)
-{
+int32_t CameraService::getNumberOfCameras() {
+ return mNumberOfCameras;
+}
+
+status_t CameraService::getCameraInfo(int cameraId,
+ struct CameraInfo* cameraInfo) {
+ if (cameraId < 0 || cameraId >= mNumberOfCameras) {
+ return BAD_VALUE;
+ }
+
+ HAL_getCameraInfo(cameraId, cameraInfo);
+ return OK;
+}
+
+sp<ICamera> CameraService::connect(
+ const sp<ICameraClient>& cameraClient, int cameraId) {
int callingPid = getCallingPid();
- LOGV("CameraService::connect E (pid %d, client %p)", callingPid,
- cameraClient->asBinder().get());
+ LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId);
- Mutex::Autolock lock(mServiceLock);
sp<Client> client;
- if (mClient != 0) {
- sp<Client> currentClient = mClient.promote();
- if (currentClient != 0) {
- sp<ICameraClient> currentCameraClient(currentClient->getCameraClient());
- if (cameraClient->asBinder() == currentCameraClient->asBinder()) {
- // This is the same client reconnecting...
- LOGV("CameraService::connect X (pid %d, same client %p) is reconnecting...",
- callingPid, cameraClient->asBinder().get());
- return currentClient;
- } else {
- // It's another client... reject it
- LOGV("CameraService::connect X (pid %d, new client %p) rejected. "
- "(old pid %d, old client %p)",
- callingPid, cameraClient->asBinder().get(),
- currentClient->mClientPid, currentCameraClient->asBinder().get());
- if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) {
- LOGV("The old client is dead!");
- }
+ if (cameraId < 0 || cameraId >= mNumberOfCameras) {
+ LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
+ callingPid, cameraId);
+ return NULL;
+ }
+
+ Mutex::Autolock lock(mServiceLock);
+ if (mClient[cameraId] != 0) {
+ client = mClient[cameraId].promote();
+ if (client != 0) {
+ if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
+ LOG1("CameraService::connect X (pid %d) (the same client)",
+ callingPid);
return client;
- }
- } else {
- // can't promote, the previous client has died...
- LOGV("New client (pid %d) connecting, old reference was dangling...",
+ } else {
+ LOGW("CameraService::connect X (pid %d) rejected (existing client).",
callingPid);
- mClient.clear();
+ return NULL;
+ }
}
+ mClient[cameraId].clear();
}
- if (mUsers > 0) {
- LOGV("Still have client, rejected");
- return client;
+ if (mBusy[cameraId]) {
+ LOGW("CameraService::connect X (pid %d) rejected"
+ " (camera %d is still busy).", callingPid, cameraId);
+ return NULL;
}
- // create a new Client object
- client = new Client(this, cameraClient, callingPid);
- mClient = client;
-#if DEBUG_CLIENT_REFERENCES
- // Enable tracking for this object, and track increments and decrements of
- // the refcount.
- client->trackMe(true, true);
-#endif
- LOGV("CameraService::connect X");
+ sp<CameraHardwareInterface> hardware = HAL_openCameraHardware(cameraId);
+ if (hardware == NULL) {
+ LOGE("Fail to open camera hardware (id=%d)", cameraId);
+ return NULL;
+ }
+ client = new Client(this, cameraClient, hardware, cameraId, callingPid);
+ mClient[cameraId] = client;
+ LOG1("CameraService::connect X");
return client;
}
-void CameraService::removeClient(const sp<ICameraClient>& cameraClient)
-{
+void CameraService::removeClient(const sp<ICameraClient>& cameraClient) {
int callingPid = getCallingPid();
+ LOG1("CameraService::removeClient E (pid %d)", callingPid);
- // Declare this outside the lock to make absolutely sure the
- // destructor won't be called with the lock held.
- sp<Client> client;
+ for (int i = 0; i < mNumberOfCameras; i++) {
+ // Declare this before the lock to make absolutely sure the
+ // destructor won't be called with the lock held.
+ sp<Client> client;
- Mutex::Autolock lock(mServiceLock);
+ Mutex::Autolock lock(mServiceLock);
- if (mClient == 0) {
- // This happens when we have already disconnected.
- LOGV("removeClient (pid %d): already disconnected", callingPid);
- return;
- }
+ // This happens when we have already disconnected (or this is
+ // just another unused camera).
+ if (mClient[i] == 0) continue;
- // Promote mClient. It can fail if we are called from this path:
- // Client::~Client() -> disconnect() -> removeClient().
- client = mClient.promote();
- if (client == 0) {
- LOGV("removeClient (pid %d): no more strong reference", callingPid);
- mClient.clear();
- return;
+ // Promote mClient. It can fail if we are called from this path:
+ // Client::~Client() -> disconnect() -> removeClient().
+ client = mClient[i].promote();
+
+ if (client == 0) {
+ mClient[i].clear();
+ continue;
+ }
+
+ if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
+ // Found our camera, clear and leave.
+ LOG1("removeClient: clear camera %d", i);
+ mClient[i].clear();
+ break;
+ }
}
- if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) {
- // ugh! that's not our client!!
- LOGW("removeClient (pid %d): mClient doesn't match!", callingPid);
- } else {
- // okay, good, forget about mClient
- mClient.clear();
+ LOG1("CameraService::removeClient X (pid %d)", callingPid);
+}
+
+sp<CameraService::Client> CameraService::getClientById(int cameraId) {
+ if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL;
+ return mClient[cameraId].promote();
+}
+
+status_t CameraService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ // Permission checks
+ switch (code) {
+ case BnCameraService::CONNECT:
+ const int pid = getCallingPid();
+ const int self_pid = getpid();
+ if (pid != self_pid) {
+ // we're called from a different process, do the real check
+ if (!checkCallingPermission(
+ String16("android.permission.CAMERA"))) {
+ const int uid = getCallingUid();
+ LOGE("Permission Denial: "
+ "can't use the camera pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ }
+ break;
}
- LOGV("removeClient (pid %d) done", callingPid);
+ return BnCameraService::onTransact(code, data, reply, flags);
}
-// The reason we need this count is a new CameraService::connect() request may
-// come in while the previous Client's destructor has not been run or is still
-// running. If the last strong reference of the previous Client is gone but
-// destructor has not been run, we should not allow the new Client to be created
-// because we need to wait for the previous Client to tear down the hardware
-// first.
-void CameraService::incUsers() {
- android_atomic_inc(&mUsers);
+// The reason we need this busy bit is a new CameraService::connect() request
+// may come in while the previous Client's destructor has not been run or is
+// still running. If the last strong reference of the previous Client is gone
+// but the destructor has not been finished, we should not allow the new Client
+// to be created because we need to wait for the previous Client to tear down
+// the hardware first.
+void CameraService::setCameraBusy(int cameraId) {
+ android_atomic_write(1, &mBusy[cameraId]);
}
-void CameraService::decUsers() {
- android_atomic_dec(&mUsers);
+void CameraService::setCameraFree(int cameraId) {
+ android_atomic_write(0, &mBusy[cameraId]);
}
-static sp<MediaPlayer> newMediaPlayer(const char *file)
-{
- sp<MediaPlayer> mp = new MediaPlayer();
- if (mp->setDataSource(file, NULL /* headers */) == NO_ERROR) {
+// We share the media players for shutter and recording sound for all clients.
+// A reference count is kept to determine when we will actually release the
+// media players.
+
+static MediaPlayer* newMediaPlayer(const char *file) {
+ MediaPlayer* mp = new MediaPlayer();
+ if (mp->setDataSource(file, NULL) == NO_ERROR) {
mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
mp->prepare();
} else {
- mp.clear();
- LOGE("Failed to load CameraService sounds.");
+ LOGE("Failed to load CameraService sounds: %s", file);
+ return NULL;
}
return mp;
}
+void CameraService::loadSound() {
+ Mutex::Autolock lock(mSoundLock);
+ LOG1("CameraService::loadSound ref=%d", mSoundRef);
+ if (mSoundRef++) return;
+
+ mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
+ mSoundPlayer[SOUND_RECORDING] = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
+}
+
+void CameraService::releaseSound() {
+ Mutex::Autolock lock(mSoundLock);
+ LOG1("CameraService::releaseSound ref=%d", mSoundRef);
+ if (--mSoundRef) return;
+
+ for (int i = 0; i < NUM_SOUNDS; i++) {
+ if (mSoundPlayer[i] != 0) {
+ mSoundPlayer[i]->disconnect();
+ mSoundPlayer[i].clear();
+ }
+ }
+}
+
+void CameraService::playSound(sound_kind kind) {
+ LOG1("playSound(%d)", kind);
+ Mutex::Autolock lock(mSoundLock);
+ sp<MediaPlayer> player = mSoundPlayer[kind];
+ if (player != 0) {
+ // do not play the sound if stream volume is 0
+ // (typically because ringer mode is silent).
+ int index;
+ AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
+ if (index != 0) {
+ player->seekTo(0);
+ player->start();
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
CameraService::Client::Client(const sp<CameraService>& cameraService,
- const sp<ICameraClient>& cameraClient, pid_t clientPid)
-{
+ const sp<ICameraClient>& cameraClient,
+ const sp<CameraHardwareInterface>& hardware,
+ int cameraId, int clientPid) {
int callingPid = getCallingPid();
- LOGV("Client::Client E (pid %d)", callingPid);
+ LOG1("Client::Client E (pid %d)", callingPid);
+
mCameraService = cameraService;
mCameraClient = cameraClient;
+ mHardware = hardware;
+ mCameraId = cameraId;
mClientPid = clientPid;
- mHardware = openCameraHardware();
mUseOverlay = mHardware->useOverlay();
+ mMsgEnabled = 0;
mHardware->setCallbacks(notifyCallback,
dataCallback,
dataCallbackTimestamp,
- mCameraService.get());
+ (void *)cameraId);
// Enable zoom, error, and focus messages by default
- mHardware->enableMsgType(CAMERA_MSG_ERROR |
- CAMERA_MSG_ZOOM |
- CAMERA_MSG_FOCUS);
-
- mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
- mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
+ enableMsgType(CAMERA_MSG_ERROR |
+ CAMERA_MSG_ZOOM |
+ CAMERA_MSG_FOCUS);
mOverlayW = 0;
mOverlayH = 0;
// Callback is disabled by default
mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
mOrientation = 0;
- cameraService->incUsers();
- LOGV("Client::Client X (pid %d)", callingPid);
+ cameraService->setCameraBusy(cameraId);
+ cameraService->loadSound();
+ LOG1("Client::Client X (pid %d)", callingPid);
}
-status_t CameraService::Client::checkPid()
-{
+static void *unregister_surface(void *arg) {
+ ISurface *surface = (ISurface *)arg;
+ surface->unregisterBuffers();
+ IPCThreadState::self()->flushCommands();
+ return NULL;
+}
+
+// tear down the client
+CameraService::Client::~Client() {
int callingPid = getCallingPid();
- if (mClientPid == callingPid) return NO_ERROR;
- LOGW("Attempt to use locked camera (client %p) from different process "
- " (old pid %d, new pid %d)",
- getCameraClient()->asBinder().get(), mClientPid, callingPid);
- return -EBUSY;
+ LOG1("Client::~Client E (pid %d, this %p)", callingPid, this);
+
+ if (mSurface != 0 && !mUseOverlay) {
+ pthread_t thr;
+ // We unregister the buffers in a different thread because binder does
+ // not let us make sychronous transactions in a binder destructor (that
+ // is, upon our reaching a refcount of zero.)
+ pthread_create(&thr,
+ NULL, // attr
+ unregister_surface,
+ mSurface.get());
+ pthread_join(thr, NULL);
+ }
+
+ // set mClientPid to let disconnet() tear down the hardware
+ mClientPid = callingPid;
+ disconnect();
+ mCameraService->releaseSound();
+ LOG1("Client::~Client X (pid %d, this %p)", callingPid, this);
}
-status_t CameraService::Client::lock()
-{
+// ----------------------------------------------------------------------------
+
+status_t CameraService::Client::checkPid() const {
+ int callingPid = getCallingPid();
+ if (callingPid == mClientPid) return NO_ERROR;
+
+ LOGW("attempt to use a locked camera from a different process"
+ " (old pid %d, new pid %d)", mClientPid, callingPid);
+ return EBUSY;
+}
+
+status_t CameraService::Client::checkPidAndHardware() const {
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+ if (mHardware == 0) {
+ LOGE("attempt to use a camera after disconnect() (pid %d)", getCallingPid());
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
+status_t CameraService::Client::lock() {
int callingPid = getCallingPid();
- LOGV("lock from pid %d (mClientPid %d)", callingPid, mClientPid);
- Mutex::Autolock _l(mLock);
+ LOG1("lock (pid %d)", callingPid);
+ Mutex::Autolock lock(mLock);
+
// lock camera to this client if the the camera is unlocked
if (mClientPid == 0) {
mClientPid = callingPid;
return NO_ERROR;
}
- // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise
+
+ // returns NO_ERROR if the client already owns the camera, EBUSY otherwise
return checkPid();
}
-status_t CameraService::Client::unlock()
-{
+status_t CameraService::Client::unlock() {
int callingPid = getCallingPid();
- LOGV("unlock from pid %d (mClientPid %d)", callingPid, mClientPid);
- Mutex::Autolock _l(mLock);
- // allow anyone to use camera
+ LOG1("unlock (pid %d)", callingPid);
+ Mutex::Autolock lock(mLock);
+
+ // allow anyone to use camera (after they lock the camera)
status_t result = checkPid();
if (result == NO_ERROR) {
mClientPid = 0;
- LOGV("clear mCameraClient (pid %d)", callingPid);
- // we need to remove the reference so that when app goes
- // away, the reference count goes to 0.
+ LOG1("clear mCameraClient (pid %d)", callingPid);
+ // we need to remove the reference to ICameraClient so that when the app
+ // goes away, the reference count goes to 0.
mCameraClient.clear();
}
return result;
}
-status_t CameraService::Client::connect(const sp<ICameraClient>& client)
-{
+// connect a new client to the camera
+status_t CameraService::Client::connect(const sp<ICameraClient>& client) {
int callingPid = getCallingPid();
+ LOG1("connect E (pid %d)", callingPid);
+ Mutex::Autolock lock(mLock);
- // connect a new process to the camera
- LOGV("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get());
-
- // I hate this hack, but things get really ugly when the media recorder
- // service is handing back the camera to the app. The ICameraClient
- // destructor will be called during the same IPC, making it look like
- // the remote client is trying to disconnect. This hack temporarily
- // sets the mClientPid to an invalid pid to prevent the hardware from
- // being torn down.
- {
-
- // hold a reference to the old client or we will deadlock if the client is
- // in the same process and we hold the lock when we remove the reference
- sp<ICameraClient> oldClient;
- {
- Mutex::Autolock _l(mLock);
- if (mClientPid != 0 && checkPid() != NO_ERROR) {
- LOGW("Tried to connect to locked camera (old pid %d, new pid %d)",
- mClientPid, callingPid);
- return -EBUSY;
- }
- oldClient = mCameraClient;
-
- // did the client actually change?
- if ((mCameraClient != NULL) && (client->asBinder() == mCameraClient->asBinder())) {
- LOGV("Connect to the same client");
- return NO_ERROR;
- }
-
- mCameraClient = client;
- mClientPid = -1;
- mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
- LOGV("Connect to the new client (pid %d, client %p)",
- callingPid, mCameraClient->asBinder().get());
- }
+ if (mClientPid != 0 && checkPid() != NO_ERROR) {
+ LOGW("Tried to connect to a locked camera (old pid %d, new pid %d)",
+ mClientPid, callingPid);
+ return EBUSY;
+ }
+ if (mCameraClient != 0 && (client->asBinder() == mCameraClient->asBinder())) {
+ LOG1("Connect to the same client");
+ return NO_ERROR;
}
- // the old client destructor is called when oldClient goes out of scope
- // now we set the new PID to lock the interface again
+
+ mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
mClientPid = callingPid;
+ mCameraClient = client;
+ LOG1("connect X (pid %d)", callingPid);
return NO_ERROR;
}
-#if HAVE_ANDROID_OS
-static void *unregister_surface(void *arg)
-{
- ISurface *surface = (ISurface *)arg;
- surface->unregisterBuffers();
- IPCThreadState::self()->flushCommands();
- return NULL;
-}
-#endif
-
-CameraService::Client::~Client()
-{
+void CameraService::Client::disconnect() {
int callingPid = getCallingPid();
+ LOG1("disconnect E (pid %d)", callingPid);
+ Mutex::Autolock lock(mLock);
- // tear down client
- LOGV("Client::~Client E (pid %d, client %p)",
- callingPid, getCameraClient()->asBinder().get());
- if (mSurface != 0 && !mUseOverlay) {
-#if HAVE_ANDROID_OS
- pthread_t thr;
- // We unregister the buffers in a different thread because binder does
- // not let us make sychronous transactions in a binder destructor (that
- // is, upon our reaching a refcount of zero.)
- pthread_create(&thr, NULL,
- unregister_surface,
- mSurface.get());
- pthread_join(thr, NULL);
-#else
- mSurface->unregisterBuffers();
-#endif
- }
-
- if (mMediaPlayerBeep.get() != NULL) {
- mMediaPlayerBeep->disconnect();
- mMediaPlayerBeep.clear();
- }
- if (mMediaPlayerClick.get() != NULL) {
- mMediaPlayerClick->disconnect();
- mMediaPlayerClick.clear();
+ if (checkPid() != NO_ERROR) {
+ LOGW("different client - don't disconnect");
+ return;
}
- // make sure we tear down the hardware
- mClientPid = callingPid;
- disconnect();
- LOGV("Client::~Client X (pid %d)", mClientPid);
-}
-
-void CameraService::Client::disconnect()
-{
- int callingPid = getCallingPid();
-
- LOGV("Client::disconnect() E (pid %d client %p)",
- callingPid, getCameraClient()->asBinder().get());
-
- Mutex::Autolock lock(mLock);
if (mClientPid <= 0) {
- LOGV("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
- return;
- }
- if (checkPid() != NO_ERROR) {
- LOGV("Different client - don't disconnect");
+ LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
return;
}
@@ -400,508 +452,551 @@ void CameraService::Client::disconnect()
// from the user directly, or called by the destructor.
if (mHardware == 0) return;
- LOGV("hardware teardown");
+ LOG1("hardware teardown");
// Before destroying mHardware, we must make sure it's in the
// idle state.
+ // Turn off all messages.
+ disableMsgType(CAMERA_MSG_ALL_MSGS);
mHardware->stopPreview();
- // Cancel all picture callbacks.
- mHardware->disableMsgType(CAMERA_MSG_SHUTTER |
- CAMERA_MSG_POSTVIEW_FRAME |
- CAMERA_MSG_RAW_IMAGE |
- CAMERA_MSG_COMPRESSED_IMAGE);
mHardware->cancelPicture();
- // Turn off remaining messages.
- mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS);
// Release the hardware resources.
mHardware->release();
// Release the held overlay resources.
- if (mUseOverlay)
- {
+ if (mUseOverlay) {
mOverlayRef = 0;
}
mHardware.clear();
mCameraService->removeClient(mCameraClient);
- mCameraService->decUsers();
+ mCameraService->setCameraFree(mCameraId);
- LOGV("Client::disconnect() X (pid %d)", callingPid);
+ LOG1("disconnect X (pid %d)", callingPid);
}
-// pass the buffered ISurface to the camera service
-status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
-{
- LOGV("setPreviewDisplay(%p) (pid %d)",
- ((surface == NULL) ? NULL : surface.get()), getCallingPid());
+// ----------------------------------------------------------------------------
+
+// set the ISurface that the preview will use
+status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) {
+ LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
Mutex::Autolock lock(mLock);
- status_t result = checkPid();
+ status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
- Mutex::Autolock surfaceLock(mSurfaceLock);
result = NO_ERROR;
- // asBinder() is safe on NULL (returns NULL)
- if (surface->asBinder() != mSurface->asBinder()) {
- if (mSurface != 0) {
- LOGV("clearing old preview surface %p", mSurface.get());
- if ( !mUseOverlay)
- {
- mSurface->unregisterBuffers();
- }
- else
- {
- // Force the destruction of any previous overlay
- sp<Overlay> dummy;
- mHardware->setOverlay( dummy );
- }
- }
- mSurface = surface;
- mOverlayRef = 0;
- // If preview has been already started, set overlay or register preview
- // buffers now.
- if (mHardware->previewEnabled()) {
- if (mUseOverlay) {
- result = setOverlay();
- } else if (mSurface != 0) {
- result = registerPreviewBuffers();
- }
- }
- }
- return result;
-}
-// set the preview callback flag to affect how the received frames from
-// preview are handled.
-void CameraService::Client::setPreviewCallbackFlag(int callback_flag)
-{
- LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid());
- Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) return;
- mPreviewCallbackFlag = callback_flag;
-
- if(mUseOverlay) {
- if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)
- mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- else
- mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- }
-}
-
-// start preview mode
-status_t CameraService::Client::startCameraMode(camera_mode mode)
-{
- int callingPid = getCallingPid();
-
- LOGV("startCameraMode(%d) (pid %d)", mode, callingPid);
-
- /* we cannot call into mHardware with mLock held because
- * mHardware has callbacks onto us which acquire this lock
- */
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
+ // return if no change in surface.
+ // asBinder() is safe on NULL (returns NULL)
+ if (surface->asBinder() == mSurface->asBinder()) {
+ return result;
}
- switch(mode) {
- case CAMERA_RECORDING_MODE:
- if (mSurface == 0) {
- LOGE("setPreviewDisplay must be called before startRecordingMode.");
- return INVALID_OPERATION;
+ if (mSurface != 0) {
+ LOG1("clearing old preview surface %p", mSurface.get());
+ if (mUseOverlay) {
+ // Force the destruction of any previous overlay
+ sp<Overlay> dummy;
+ mHardware->setOverlay(dummy);
+ } else {
+ mSurface->unregisterBuffers();
}
- return startRecordingMode();
-
- default: // CAMERA_PREVIEW_MODE
- if (mSurface == 0) {
- LOGV("mSurface is not set yet.");
+ }
+ mSurface = surface;
+ mOverlayRef = 0;
+ // If preview has been already started, set overlay or register preview
+ // buffers now.
+ if (mHardware->previewEnabled()) {
+ if (mUseOverlay) {
+ result = setOverlay();
+ } else if (mSurface != 0) {
+ result = registerPreviewBuffers();
}
- return startPreviewMode();
}
-}
-
-status_t CameraService::Client::startRecordingMode()
-{
- LOGV("startRecordingMode (pid %d)", getCallingPid());
- status_t ret = UNKNOWN_ERROR;
+ return result;
+}
- // if preview has not been started, start preview first
- if (!mHardware->previewEnabled()) {
- ret = startPreviewMode();
- if (ret != NO_ERROR) {
- return ret;
- }
- }
+status_t CameraService::Client::registerPreviewBuffers() {
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
- // if recording has been enabled, nothing needs to be done
- if (mHardware->recordingEnabled()) {
- return NO_ERROR;
- }
+ // FIXME: don't use a hardcoded format here.
+ ISurface::BufferHeap buffers(w, h, w, h,
+ HAL_PIXEL_FORMAT_YCrCb_420_SP,
+ mOrientation,
+ 0,
+ mHardware->getPreviewHeap());
- // start recording mode
- ret = mHardware->startRecording();
- if (ret != NO_ERROR) {
- LOGE("mHardware->startRecording() failed with status %d", ret);
+ status_t result = mSurface->registerBuffers(buffers);
+ if (result != NO_ERROR) {
+ LOGE("registerBuffers failed with status %d", result);
}
- return ret;
+ return result;
}
-status_t CameraService::Client::setOverlay()
-{
- LOGV("setOverlay");
+status_t CameraService::Client::setOverlay() {
int w, h;
CameraParameters params(mHardware->getParameters());
params.getPreviewSize(&w, &h);
- if ( w != mOverlayW || h != mOverlayH )
- {
+ if (w != mOverlayW || h != mOverlayH) {
// Force the destruction of any previous overlay
sp<Overlay> dummy;
- mHardware->setOverlay( dummy );
+ mHardware->setOverlay(dummy);
mOverlayRef = 0;
}
- status_t ret = NO_ERROR;
- if (mSurface != 0) {
- if (mOverlayRef.get() == NULL) {
-
+ status_t result = NO_ERROR;
+ if (mSurface == 0) {
+ result = mHardware->setOverlay(NULL);
+ } else {
+ if (mOverlayRef == 0) {
// FIXME:
// Surfaceflinger may hold onto the previous overlay reference for some
// time after we try to destroy it. retry a few times. In the future, we
// should make the destroy call block, or possibly specify that we can
- // wait in the createOverlay call if the previous overlay is in the
+ // wait in the createOverlay call if the previous overlay is in the
// process of being destroyed.
for (int retry = 0; retry < 50; ++retry) {
mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT,
mOrientation);
- if (mOverlayRef != NULL) break;
+ if (mOverlayRef != 0) break;
LOGW("Overlay create failed - retrying");
usleep(20000);
}
- if ( mOverlayRef.get() == NULL )
- {
+ if (mOverlayRef == 0) {
LOGE("Overlay Creation Failed!");
return -EINVAL;
}
- ret = mHardware->setOverlay(new Overlay(mOverlayRef));
+ result = mHardware->setOverlay(new Overlay(mOverlayRef));
}
- } else {
- ret = mHardware->setOverlay(NULL);
}
- if (ret != NO_ERROR) {
- LOGE("mHardware->setOverlay() failed with status %d\n", ret);
+ if (result != NO_ERROR) {
+ LOGE("mHardware->setOverlay() failed with status %d\n", result);
+ return result;
}
mOverlayW = w;
mOverlayH = h;
- return ret;
+ return result;
}
-status_t CameraService::Client::registerPreviewBuffers()
-{
- int w, h;
- CameraParameters params(mHardware->getParameters());
- params.getPreviewSize(&w, &h);
+// set the preview callback flag to affect how the received frames from
+// preview are handled.
+void CameraService::Client::setPreviewCallbackFlag(int callback_flag) {
+ LOG1("setPreviewCallbackFlag(%d) (pid %d)", callback_flag, getCallingPid());
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
- // don't use a hardcoded format here
- ISurface::BufferHeap buffers(w, h, w, h,
- HAL_PIXEL_FORMAT_YCrCb_420_SP,
- mOrientation,
- 0,
- mHardware->getPreviewHeap());
+ mPreviewCallbackFlag = callback_flag;
- status_t ret = mSurface->registerBuffers(buffers);
- if (ret != NO_ERROR) {
- LOGE("registerBuffers failed with status %d", ret);
+ // If we don't use overlay, we always need the preview frame for display.
+ // If we do use overlay, we only need the preview frame if the user
+ // wants the data.
+ if (mUseOverlay) {
+ if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK) {
+ enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ } else {
+ disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ }
}
- return ret;
}
-status_t CameraService::Client::startPreviewMode()
-{
- LOGV("startPreviewMode (pid %d)", getCallingPid());
+// start preview mode
+status_t CameraService::Client::startPreview() {
+ LOG1("startPreview (pid %d)", getCallingPid());
+ return startCameraMode(CAMERA_PREVIEW_MODE);
+}
+
+// start recording mode
+status_t CameraService::Client::startRecording() {
+ LOG1("startRecording (pid %d)", getCallingPid());
+ return startCameraMode(CAMERA_RECORDING_MODE);
+}
+
+// start preview or recording
+status_t CameraService::Client::startCameraMode(camera_mode mode) {
+ LOG1("startCameraMode(%d)", mode);
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ switch(mode) {
+ case CAMERA_PREVIEW_MODE:
+ if (mSurface == 0) {
+ LOG1("mSurface is not set yet.");
+ // still able to start preview in this case.
+ }
+ return startPreviewMode();
+ case CAMERA_RECORDING_MODE:
+ if (mSurface == 0) {
+ LOGE("mSurface must be set before startRecordingMode.");
+ return INVALID_OPERATION;
+ }
+ return startRecordingMode();
+ default:
+ return UNKNOWN_ERROR;
+ }
+}
+
+status_t CameraService::Client::startPreviewMode() {
+ LOG1("startPreviewMode");
+ status_t result = NO_ERROR;
// if preview has been enabled, nothing needs to be done
if (mHardware->previewEnabled()) {
return NO_ERROR;
}
- // start preview mode
-#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
- debug_frame_cnt = 0;
-#endif
- status_t ret = NO_ERROR;
-
if (mUseOverlay) {
// If preview display has been set, set overlay now.
if (mSurface != 0) {
- ret = setOverlay();
+ result = setOverlay();
}
- if (ret != NO_ERROR) return ret;
- ret = mHardware->startPreview();
+ if (result != NO_ERROR) return result;
+ result = mHardware->startPreview();
} else {
- mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- ret = mHardware->startPreview();
- if (ret != NO_ERROR) return ret;
+ enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ result = mHardware->startPreview();
+ if (result != NO_ERROR) return result;
// If preview display has been set, register preview buffers now.
if (mSurface != 0) {
- // Unregister here because the surface registered with raw heap.
+ // Unregister here because the surface may be previously registered
+ // with the raw (snapshot) heap.
mSurface->unregisterBuffers();
- ret = registerPreviewBuffers();
+ result = registerPreviewBuffers();
}
}
- return ret;
+ return result;
}
-status_t CameraService::Client::startPreview()
-{
- LOGV("startPreview (pid %d)", getCallingPid());
+status_t CameraService::Client::startRecordingMode() {
+ LOG1("startRecordingMode");
+ status_t result = NO_ERROR;
- return startCameraMode(CAMERA_PREVIEW_MODE);
-}
-
-status_t CameraService::Client::startRecording()
-{
- LOGV("startRecording (pid %d)", getCallingPid());
+ // if recording has been enabled, nothing needs to be done
+ if (mHardware->recordingEnabled()) {
+ return NO_ERROR;
+ }
- if (mMediaPlayerBeep.get() != NULL) {
- // do not play record jingle if stream volume is 0
- // (typically because ringer mode is silent).
- int index;
- AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
- if (index != 0) {
- mMediaPlayerBeep->seekTo(0);
- mMediaPlayerBeep->start();
+ // if preview has not been started, start preview first
+ if (!mHardware->previewEnabled()) {
+ result = startPreviewMode();
+ if (result != NO_ERROR) {
+ return result;
}
}
- mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME);
-
- return startCameraMode(CAMERA_RECORDING_MODE);
+ // start recording mode
+ enableMsgType(CAMERA_MSG_VIDEO_FRAME);
+ mCameraService->playSound(SOUND_RECORDING);
+ result = mHardware->startRecording();
+ if (result != NO_ERROR) {
+ LOGE("mHardware->startRecording() failed with status %d", result);
+ }
+ return result;
}
// stop preview mode
-void CameraService::Client::stopPreview()
-{
- LOGV("stopPreview (pid %d)", getCallingPid());
-
- // hold main lock during state transition
- {
- Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) return;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return;
- }
+void CameraService::Client::stopPreview() {
+ LOG1("stopPreview (pid %d)", getCallingPid());
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
- mHardware->stopPreview();
- mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- LOGV("stopPreview(), hardware stopped OK");
+ disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ mHardware->stopPreview();
- if (mSurface != 0 && !mUseOverlay) {
- mSurface->unregisterBuffers();
- }
+ if (mSurface != 0 && !mUseOverlay) {
+ mSurface->unregisterBuffers();
}
- // hold preview buffer lock
- {
- Mutex::Autolock lock(mPreviewLock);
- mPreviewBuffer.clear();
- }
+ mPreviewBuffer.clear();
}
// stop recording mode
-void CameraService::Client::stopRecording()
-{
- LOGV("stopRecording (pid %d)", getCallingPid());
-
- // hold main lock during state transition
- {
- Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) return;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return;
- }
-
- if (mMediaPlayerBeep.get() != NULL) {
- mMediaPlayerBeep->seekTo(0);
- mMediaPlayerBeep->start();
- }
+void CameraService::Client::stopRecording() {
+ LOG1("stopRecording (pid %d)", getCallingPid());
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
- mHardware->stopRecording();
- mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME);
- LOGV("stopRecording(), hardware stopped OK");
- }
+ mCameraService->playSound(SOUND_RECORDING);
+ disableMsgType(CAMERA_MSG_VIDEO_FRAME);
+ mHardware->stopRecording();
- // hold preview buffer lock
- {
- Mutex::Autolock lock(mPreviewLock);
- mPreviewBuffer.clear();
- }
+ mPreviewBuffer.clear();
}
// release a recording frame
-void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem)
-{
+void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) {
Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) return;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return;
- }
-
+ if (checkPidAndHardware() != NO_ERROR) return;
mHardware->releaseRecordingFrame(mem);
}
-bool CameraService::Client::previewEnabled()
-{
+bool CameraService::Client::previewEnabled() {
+ LOG1("previewEnabled (pid %d)", getCallingPid());
+
Mutex::Autolock lock(mLock);
- if (mHardware == 0) return false;
+ if (checkPidAndHardware() != NO_ERROR) return false;
return mHardware->previewEnabled();
}
-bool CameraService::Client::recordingEnabled()
-{
+bool CameraService::Client::recordingEnabled() {
+ LOG1("recordingEnabled (pid %d)", getCallingPid());
+
Mutex::Autolock lock(mLock);
- if (mHardware == 0) return false;
+ if (checkPidAndHardware() != NO_ERROR) return false;
return mHardware->recordingEnabled();
}
-// Safely retrieves a strong pointer to the client during a hardware callback.
-sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user)
-{
- sp<Client> client = 0;
- CameraService *service = static_cast<CameraService*>(user);
- if (service != NULL) {
- Mutex::Autolock ourLock(service->mServiceLock);
- if (service->mClient != 0) {
- client = service->mClient.promote();
- if (client == 0) {
- LOGE("getClientFromCookie: client appears to have died");
- service->mClient.clear();
- }
- } else {
- LOGE("getClientFromCookie: got callback but client was NULL");
- }
- }
- return client;
-}
+status_t CameraService::Client::autoFocus() {
+ LOG1("autoFocus (pid %d)", getCallingPid());
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
-#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE || \
- DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE || \
- DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
-static void dump_to_file(const char *fname,
- uint8_t *buf, uint32_t size)
-{
- int nw, cnt = 0;
- uint32_t written = 0;
+ return mHardware->autoFocus();
+}
- LOGV("opening file [%s]\n", fname);
- int fd = open(fname, O_RDWR | O_CREAT);
- if (fd < 0) {
- LOGE("failed to create file [%s]: %s", fname, strerror(errno));
- return;
- }
+status_t CameraService::Client::cancelAutoFocus() {
+ LOG1("cancelAutoFocus (pid %d)", getCallingPid());
- LOGV("writing %d bytes to file [%s]\n", size, fname);
- while (written < size) {
- nw = ::write(fd,
- buf + written,
- size - written);
- if (nw < 0) {
- LOGE("failed to write to file [%s]: %s",
- fname, strerror(errno));
- break;
- }
- written += nw;
- cnt++;
- }
- LOGV("done writing %d bytes to file [%s] in %d passes\n",
- size, fname, cnt);
- ::close(fd);
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ return mHardware->cancelAutoFocus();
}
-#endif
-status_t CameraService::Client::autoFocus()
-{
- LOGV("autoFocus (pid %d)", getCallingPid());
+// take a picture - image is returned in callback
+status_t CameraService::Client::takePicture() {
+ LOG1("takePicture (pid %d)", getCallingPid());
Mutex::Autolock lock(mLock);
- status_t result = checkPid();
+ status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
+ enableMsgType(CAMERA_MSG_SHUTTER |
+ CAMERA_MSG_POSTVIEW_FRAME |
+ CAMERA_MSG_RAW_IMAGE |
+ CAMERA_MSG_COMPRESSED_IMAGE);
- return mHardware->autoFocus();
+ return mHardware->takePicture();
}
-status_t CameraService::Client::cancelAutoFocus()
-{
- LOGV("cancelAutoFocus (pid %d)", getCallingPid());
+// set preview/capture parameters - key/value pairs
+status_t CameraService::Client::setParameters(const String8& params) {
+ LOG1("setParameters (pid %d) (%s)", getCallingPid(), params.string());
Mutex::Autolock lock(mLock);
- status_t result = checkPid();
+ status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- return mHardware->cancelAutoFocus();
+ CameraParameters p(params);
+ return mHardware->setParameters(p);
}
-// take a picture - image is returned in callback
-status_t CameraService::Client::takePicture()
-{
- LOGV("takePicture (pid %d)", getCallingPid());
+// get preview/capture parameters - key/value pairs
+String8 CameraService::Client::getParameters() const {
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return String8();
+ String8 params(mHardware->getParameters().flatten());
+ LOG1("getParameters (pid %d) (%s)", getCallingPid(), params.string());
+ return params;
+}
+
+status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
+ LOG1("sendCommand (pid %d)", getCallingPid());
Mutex::Autolock lock(mLock);
- status_t result = checkPid();
+ status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
+ if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
+ // The orientation cannot be set during preview.
+ if (mHardware->previewEnabled()) {
+ return INVALID_OPERATION;
+ }
+ switch (arg1) {
+ case 0:
+ mOrientation = ISurface::BufferHeap::ROT_0;
+ break;
+ case 90:
+ mOrientation = ISurface::BufferHeap::ROT_90;
+ break;
+ case 180:
+ mOrientation = ISurface::BufferHeap::ROT_180;
+ break;
+ case 270:
+ mOrientation = ISurface::BufferHeap::ROT_270;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+ return OK;
}
- mHardware->enableMsgType(CAMERA_MSG_SHUTTER |
- CAMERA_MSG_POSTVIEW_FRAME |
- CAMERA_MSG_RAW_IMAGE |
- CAMERA_MSG_COMPRESSED_IMAGE);
+ return mHardware->sendCommand(cmd, arg1, arg2);
+}
- return mHardware->takePicture();
+// ----------------------------------------------------------------------------
+
+void CameraService::Client::enableMsgType(int32_t msgType) {
+ android_atomic_or(msgType, &mMsgEnabled);
+ mHardware->enableMsgType(msgType);
}
-// snapshot taken
-void CameraService::Client::handleShutter(
- image_rect_type *size // The width and height of yuv picture for
- // registerBuffer. If this is NULL, use the picture
- // size from parameters.
-)
-{
- // Play shutter sound.
- if (mMediaPlayerClick.get() != NULL) {
- // do not play shutter sound if stream volume is 0
- // (typically because ringer mode is silent).
- int index;
- AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
- if (index != 0) {
- mMediaPlayerClick->seekTo(0);
- mMediaPlayerClick->start();
+void CameraService::Client::disableMsgType(int32_t msgType) {
+ android_atomic_and(~msgType, &mMsgEnabled);
+ mHardware->disableMsgType(msgType);
+}
+
+#define CHECK_MESSAGE_INTERVAL 10 // 10ms
+bool CameraService::Client::lockIfMessageWanted(int32_t msgType) {
+ int sleepCount = 0;
+ while (mMsgEnabled & msgType) {
+ if (mLock.tryLock() == NO_ERROR) {
+ if (sleepCount > 0) {
+ LOG1("lockIfMessageWanted(%d): waited for %d ms",
+ msgType, sleepCount * CHECK_MESSAGE_INTERVAL);
+ }
+ return true;
+ }
+ if (sleepCount++ == 0) {
+ LOG1("lockIfMessageWanted(%d): enter sleep", msgType);
}
+ usleep(CHECK_MESSAGE_INTERVAL * 1000);
+ }
+ LOGW("lockIfMessageWanted(%d): dropped unwanted message", msgType);
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+
+// Converts from a raw pointer to the client to a strong pointer during a
+// hardware callback. This requires the callbacks only happen when the client
+// is still alive.
+sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) {
+ sp<Client> client = gCameraService->getClientById((int) user);
+
+ // This could happen if the Client is in the process of shutting down (the
+ // last strong reference is gone, but the destructor hasn't finished
+ // stopping the hardware).
+ if (client == 0) return NULL;
+
+ // The checks below are not necessary and are for debugging only.
+ if (client->mCameraService.get() != gCameraService) {
+ LOGE("mismatch service!");
+ return NULL;
+ }
+
+ if (client->mHardware == 0) {
+ LOGE("mHardware == 0: callback after disconnect()?");
+ return NULL;
+ }
+
+ return client;
+}
+
+// Callback messages can be dispatched to internal handlers or pass to our
+// client's callback functions, depending on the message type.
+//
+// notifyCallback:
+// CAMERA_MSG_SHUTTER handleShutter
+// (others) c->notifyCallback
+// dataCallback:
+// CAMERA_MSG_PREVIEW_FRAME handlePreviewData
+// CAMERA_MSG_POSTVIEW_FRAME handlePostview
+// CAMERA_MSG_RAW_IMAGE handleRawPicture
+// CAMERA_MSG_COMPRESSED_IMAGE handleCompressedPicture
+// (others) c->dataCallback
+// dataCallbackTimestamp
+// (others) c->dataCallbackTimestamp
+//
+// NOTE: the *Callback functions grab mLock of the client before passing
+// control to handle* functions. So the handle* functions must release the
+// lock before calling the ICameraClient's callbacks, so those callbacks can
+// invoke methods in the Client class again (For example, the preview frame
+// callback may want to releaseRecordingFrame). The handle* functions must
+// release the lock after all accesses to member variables, so it must be
+// handled very carefully.
+
+void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1,
+ int32_t ext2, void* user) {
+ LOG2("notifyCallback(%d)", msgType);
+
+ sp<Client> client = getClientFromCookie(user);
+ if (client == 0) return;
+ if (!client->lockIfMessageWanted(msgType)) return;
+
+ switch (msgType) {
+ case CAMERA_MSG_SHUTTER:
+ // ext1 is the dimension of the yuv picture.
+ client->handleShutter((image_rect_type *)ext1);
+ break;
+ default:
+ client->handleGenericNotify(msgType, ext1, ext2);
+ break;
}
+}
+
+void CameraService::Client::dataCallback(int32_t msgType,
+ const sp<IMemory>& dataPtr, void* user) {
+ LOG2("dataCallback(%d)", msgType);
+
+ sp<Client> client = getClientFromCookie(user);
+ if (client == 0) return;
+ if (!client->lockIfMessageWanted(msgType)) return;
+
+ if (dataPtr == 0) {
+ LOGE("Null data returned in data callback");
+ client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
+ return;
+ }
+
+ switch (msgType) {
+ case CAMERA_MSG_PREVIEW_FRAME:
+ client->handlePreviewData(dataPtr);
+ break;
+ case CAMERA_MSG_POSTVIEW_FRAME:
+ client->handlePostview(dataPtr);
+ break;
+ case CAMERA_MSG_RAW_IMAGE:
+ client->handleRawPicture(dataPtr);
+ break;
+ case CAMERA_MSG_COMPRESSED_IMAGE:
+ client->handleCompressedPicture(dataPtr);
+ break;
+ default:
+ client->handleGenericData(msgType, dataPtr);
+ break;
+ }
+}
+
+void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp,
+ int32_t msgType, const sp<IMemory>& dataPtr, void* user) {
+ LOG2("dataCallbackTimestamp(%d)", msgType);
+
+ sp<Client> client = getClientFromCookie(user);
+ if (client == 0) return;
+ if (!client->lockIfMessageWanted(msgType)) return;
+
+ if (dataPtr == 0) {
+ LOGE("Null data returned in data with timestamp callback");
+ client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
+ return;
+ }
+
+ client->handleGenericDataTimestamp(timestamp, msgType, dataPtr);
+}
+
+// snapshot taken callback
+// "size" is the width and height of yuv picture for registerBuffer.
+// If it is NULL, use the picture size from parameters.
+void CameraService::Client::handleShutter(image_rect_type *size) {
+ mCameraService->playSound(SOUND_SHUTTER);
// Screen goes black after the buffer is unregistered.
if (mSurface != 0 && !mUseOverlay) {
@@ -909,10 +1004,12 @@ void CameraService::Client::handleShutter(
}
sp<ICameraClient> c = mCameraClient;
- if (c != NULL) {
+ if (c != 0) {
+ mLock.unlock();
c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
+ if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return;
}
- mHardware->disableMsgType(CAMERA_MSG_SHUTTER);
+ disableMsgType(CAMERA_MSG_SHUTTER);
// It takes some time before yuvPicture callback to be called.
// Register the buffer for raw image here to reduce latency.
@@ -926,7 +1023,7 @@ void CameraService::Client::handleShutter(
h = size->height;
w &= ~1;
h &= ~1;
- LOGV("Snapshot image width=%d, height=%d", w, h);
+ LOG1("Snapshot image width=%d, height=%d", w, h);
}
// FIXME: don't use hardcoded format constants here
ISurface::BufferHeap buffers(w, h, w, h,
@@ -934,38 +1031,20 @@ void CameraService::Client::handleShutter(
mHardware->getRawHeap());
mSurface->registerBuffers(buffers);
+ IPCThreadState::self()->flushCommands();
}
+
+ mLock.unlock();
}
// preview callback - frame buffer update
-void CameraService::Client::handlePreviewData(const sp<IMemory>& mem)
-{
+void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) {
ssize_t offset;
size_t size;
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-#if DEBUG_HEAP_LEAKS && 0 // debugging
- if (gWeakHeap == NULL) {
- if (gWeakHeap != heap) {
- LOGV("SETTING PREVIEW HEAP");
- heap->trackMe(true, true);
- gWeakHeap = heap;
- }
- }
-#endif
-#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
- {
- if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) {
- dump_to_file("/data/preview.yuv",
- (uint8_t *)heap->base() + offset, size);
- }
- }
-#endif
-
- if (!mUseOverlay)
- {
- Mutex::Autolock surfaceLock(mSurfaceLock);
- if (mSurface != NULL) {
+ if (!mUseOverlay) {
+ if (mSurface != 0) {
mSurface->postBuffer(offset);
}
}
@@ -976,7 +1055,8 @@ void CameraService::Client::handlePreviewData(const sp<IMemory>& mem)
// is callback enabled?
if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
// If the enable bit is off, the copy-out and one-shot bits are ignored
- LOGV("frame callback is diabled");
+ LOG2("frame callback is disabled");
+ mLock.unlock();
return;
}
@@ -984,61 +1064,49 @@ void CameraService::Client::handlePreviewData(const sp<IMemory>& mem)
sp<ICameraClient> c = mCameraClient;
// clear callback flags if no client or one-shot mode
- if ((c == NULL) || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
- LOGV("Disable preview callback");
+ if (c == 0 || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
+ LOG2("Disable preview callback");
mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
- FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
- FRAME_CALLBACK_FLAG_ENABLE_MASK);
- // TODO: Shouldn't we use this API for non-overlay hardware as well?
- if (mUseOverlay)
- mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
+ FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ if (mUseOverlay) {
+ disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ }
}
- // Is the received frame copied out or not?
- if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
- LOGV("frame is copied");
- copyFrameAndPostCopiedFrame(c, heap, offset, size);
+ if (c != 0) {
+ // Is the received frame copied out or not?
+ if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
+ LOG2("frame is copied");
+ copyFrameAndPostCopiedFrame(c, heap, offset, size);
+ } else {
+ LOG2("frame is forwarded");
+ mLock.unlock();
+ c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem);
+ }
} else {
- LOGV("frame is forwarded");
- c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem);
+ mLock.unlock();
}
}
// picture callback - postview image ready
-void CameraService::Client::handlePostview(const sp<IMemory>& mem)
-{
-#if DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE // for testing pursposes only
- {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- dump_to_file("/data/postview.yuv",
- (uint8_t *)heap->base() + offset, size);
- }
-#endif
+void CameraService::Client::handlePostview(const sp<IMemory>& mem) {
+ disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
sp<ICameraClient> c = mCameraClient;
- if (c != NULL) {
+ mLock.unlock();
+ if (c != 0) {
c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem);
}
- mHardware->disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
}
// picture callback - raw image ready
-void CameraService::Client::handleRawPicture(const sp<IMemory>& mem)
-{
+void CameraService::Client::handleRawPicture(const sp<IMemory>& mem) {
+ disableMsgType(CAMERA_MSG_RAW_IMAGE);
+
ssize_t offset;
size_t size;
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-#if DEBUG_HEAP_LEAKS && 0 // debugging
- gWeakHeap = heap; // debugging
-#endif
-
- //LOGV("handleRawPicture(%d, %d)", offset, size);
-#if DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE // for testing pursposes only
- dump_to_file("/data/photo.yuv",
- (uint8_t *)heap->base() + offset, size);
-#endif
// Put the YUV version of the snapshot in the preview display.
if (mSurface != 0 && !mUseOverlay) {
@@ -1046,250 +1114,90 @@ void CameraService::Client::handleRawPicture(const sp<IMemory>& mem)
}
sp<ICameraClient> c = mCameraClient;
- if (c != NULL) {
+ mLock.unlock();
+ if (c != 0) {
c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem);
}
- mHardware->disableMsgType(CAMERA_MSG_RAW_IMAGE);
}
// picture callback - compressed picture ready
-void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem)
-{
-#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only
- {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- dump_to_file("/data/photo.jpg",
- (uint8_t *)heap->base() + offset, size);
- }
-#endif
+void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem) {
+ disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
sp<ICameraClient> c = mCameraClient;
- if (c != NULL) {
+ mLock.unlock();
+ if (c != 0) {
c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem);
}
- mHardware->disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
}
-void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user)
-{
- LOGV("notifyCallback(%d)", msgType);
-
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
-
- switch (msgType) {
- case CAMERA_MSG_SHUTTER:
- // ext1 is the dimension of the yuv picture.
- client->handleShutter((image_rect_type *)ext1);
- break;
- default:
- sp<ICameraClient> c = client->mCameraClient;
- if (c != NULL) {
- c->notifyCallback(msgType, ext1, ext2);
- }
- break;
- }
-#if DEBUG_CLIENT_REFERENCES
- if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (NOTIFY CALLBACK) THIS WILL CAUSE A LOCKUP!");
- client->printRefs();
+void CameraService::Client::handleGenericNotify(int32_t msgType,
+ int32_t ext1, int32_t ext2) {
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
+ c->notifyCallback(msgType, ext1, ext2);
}
-#endif
}
-void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user)
-{
- LOGV("dataCallback(%d)", msgType);
-
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
-
- sp<ICameraClient> c = client->mCameraClient;
- if (dataPtr == NULL) {
- LOGE("Null data returned in data callback");
- if (c != NULL) {
- c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
- c->dataCallback(msgType, NULL);
- }
- return;
- }
-
- switch (msgType) {
- case CAMERA_MSG_PREVIEW_FRAME:
- client->handlePreviewData(dataPtr);
- break;
- case CAMERA_MSG_POSTVIEW_FRAME:
- client->handlePostview(dataPtr);
- break;
- case CAMERA_MSG_RAW_IMAGE:
- client->handleRawPicture(dataPtr);
- break;
- case CAMERA_MSG_COMPRESSED_IMAGE:
- client->handleCompressedPicture(dataPtr);
- break;
- default:
- if (c != NULL) {
- c->dataCallback(msgType, dataPtr);
- }
- break;
- }
-
-#if DEBUG_CLIENT_REFERENCES
- if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (DATA CALLBACK) THIS WILL CAUSE A LOCKUP!");
- client->printRefs();
+void CameraService::Client::handleGenericData(int32_t msgType,
+ const sp<IMemory>& dataPtr) {
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
+ c->dataCallback(msgType, dataPtr);
}
-#endif
}
-void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
- const sp<IMemory>& dataPtr, void* user)
-{
- LOGV("dataCallbackTimestamp(%d)", msgType);
-
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
- sp<ICameraClient> c = client->mCameraClient;
-
- if (dataPtr == NULL) {
- LOGE("Null data returned in data with timestamp callback");
- if (c != NULL) {
- c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
- c->dataCallbackTimestamp(0, msgType, NULL);
- }
- return;
- }
-
- if (c != NULL) {
+void CameraService::Client::handleGenericDataTimestamp(nsecs_t timestamp,
+ int32_t msgType, const sp<IMemory>& dataPtr) {
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
c->dataCallbackTimestamp(timestamp, msgType, dataPtr);
}
-
-#if DEBUG_CLIENT_REFERENCES
- if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (DATA CALLBACK TIMESTAMP) THIS WILL CAUSE A LOCKUP!");
- client->printRefs();
- }
-#endif
}
-// set preview/capture parameters - key/value pairs
-status_t CameraService::Client::setParameters(const String8& params)
-{
- LOGV("setParameters(%s)", params.string());
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- CameraParameters p(params);
-
- return mHardware->setParameters(p);
-}
-
-// get preview/capture parameters - key/value pairs
-String8 CameraService::Client::getParameters() const
-{
- Mutex::Autolock lock(mLock);
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return String8();
- }
-
- String8 params(mHardware->getParameters().flatten());
- LOGV("getParameters(%s)", params.string());
- return params;
-}
-
-status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
-{
- LOGV("sendCommand (pid %d)", getCallingPid());
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
- // The orientation cannot be set during preview.
- if (mHardware->previewEnabled()) {
- return INVALID_OPERATION;
- }
- switch (arg1) {
- case 0:
- mOrientation = ISurface::BufferHeap::ROT_0;
- break;
- case 90:
- mOrientation = ISurface::BufferHeap::ROT_90;
- break;
- case 180:
- mOrientation = ISurface::BufferHeap::ROT_180;
- break;
- case 270:
- mOrientation = ISurface::BufferHeap::ROT_270;
- break;
- default:
- return BAD_VALUE;
- }
- return OK;
- }
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- return mHardware->sendCommand(cmd, arg1, arg2);
-}
-
-void CameraService::Client::copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client,
- const sp<IMemoryHeap>& heap, size_t offset, size_t size)
-{
- LOGV("copyFrameAndPostCopiedFrame");
+void CameraService::Client::copyFrameAndPostCopiedFrame(
+ const sp<ICameraClient>& client, const sp<IMemoryHeap>& heap,
+ size_t offset, size_t size) {
+ LOG2("copyFrameAndPostCopiedFrame");
// It is necessary to copy out of pmem before sending this to
// the callback. For efficiency, reuse the same MemoryHeapBase
// provided it's big enough. Don't allocate the memory or
// perform the copy if there's no callback.
-
// hold the preview lock while we grab a reference to the preview buffer
sp<MemoryHeapBase> previewBuffer;
- {
- Mutex::Autolock lock(mPreviewLock);
- if (mPreviewBuffer == 0) {
- mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
- } else if (size > mPreviewBuffer->virtualSize()) {
- mPreviewBuffer.clear();
- mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
- }
- if (mPreviewBuffer == 0) {
- LOGE("failed to allocate space for preview buffer");
- return;
- }
- previewBuffer = mPreviewBuffer;
+
+ if (mPreviewBuffer == 0) {
+ mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
+ } else if (size > mPreviewBuffer->virtualSize()) {
+ mPreviewBuffer.clear();
+ mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
+ }
+ if (mPreviewBuffer == 0) {
+ LOGE("failed to allocate space for preview buffer");
+ mLock.unlock();
+ return;
}
- memcpy(previewBuffer->base(),
- (uint8_t *)heap->base() + offset, size);
+ previewBuffer = mPreviewBuffer;
+
+ memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size);
sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);
if (frame == 0) {
LOGE("failed to allocate space for frame callback");
+ mLock.unlock();
return;
}
+
+ mLock.unlock();
client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame);
}
+// ----------------------------------------------------------------------------
+
static const int kDumpLockRetries = 50;
static const int kDumpLockSleep = 60000;
@@ -1306,8 +1214,7 @@ static bool tryLock(Mutex& mutex)
return locked;
}
-status_t CameraService::dump(int fd, const Vector<String16>& args)
-{
+status_t CameraService::dump(int fd, const Vector<String16>& args) {
static const char* kDeadlockedString = "CameraService may be deadlocked\n";
const size_t SIZE = 256;
@@ -1317,7 +1224,7 @@ status_t CameraService::dump(int fd, const Vector<String16>& args)
snprintf(buffer, SIZE, "Permission Denial: "
"can't dump CameraService from pid=%d, uid=%d\n",
getCallingPid(),
- IPCThreadState::self()->getCallingUid());
+ getCallingUid());
result.append(buffer);
write(fd, result.string(), result.size());
} else {
@@ -1328,89 +1235,39 @@ status_t CameraService::dump(int fd, const Vector<String16>& args)
write(fd, result.string(), result.size());
}
- if (mClient != 0) {
- sp<Client> currentClient = mClient.promote();
- sprintf(buffer, "Client (%p) PID: %d\n",
- currentClient->getCameraClient()->asBinder().get(),
- currentClient->mClientPid);
+ bool hasClient = false;
+ for (int i = 0; i < mNumberOfCameras; i++) {
+ sp<Client> client = mClient[i].promote();
+ if (client == 0) continue;
+ hasClient = true;
+ sprintf(buffer, "Client[%d] (%p) PID: %d\n",
+ i,
+ client->getCameraClient()->asBinder().get(),
+ client->mClientPid);
result.append(buffer);
write(fd, result.string(), result.size());
- currentClient->mHardware->dump(fd, args);
- } else {
+ client->mHardware->dump(fd, args);
+ }
+ if (!hasClient) {
result.append("No camera client yet.\n");
write(fd, result.string(), result.size());
}
if (locked) mServiceLock.unlock();
- }
- return NO_ERROR;
-}
-
-status_t CameraService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- // permission checks...
- switch (code) {
- case BnCameraService::CONNECT:
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int self_pid = getpid();
- if (pid != self_pid) {
- // we're called from a different process, do the real check
- if (!checkCallingPermission(
- String16("android.permission.CAMERA")))
- {
- const int uid = ipc->getCallingUid();
- LOGE("Permission Denial: "
- "can't use the camera pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- }
- break;
- }
-
- status_t err = BnCameraService::onTransact(code, data, reply, flags);
-
-#if DEBUG_HEAP_LEAKS
- LOGV("+++ onTransact err %d code %d", err, code);
-
- if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
- // the 'service' command interrogates this binder for its name, and then supplies it
- // even for the debugging commands. that means we need to check for it here, using
- // ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to
- // BnSurfaceComposer before falling through to this code).
-
- LOGV("+++ onTransact code %d", code);
-
- CHECK_INTERFACE(ICameraService, data, reply);
-
- switch(code) {
- case 1000:
- {
- if (gWeakHeap != 0) {
- sp<IMemoryHeap> h = gWeakHeap.promote();
- IMemoryHeap *p = gWeakHeap.unsafe_get();
- LOGV("CHECKING WEAK REFERENCE %p (%p)", h.get(), p);
- if (h != 0)
- h->printRefs();
- bool attempt_to_delete = data.readInt32() == 1;
- if (attempt_to_delete) {
- // NOT SAFE!
- LOGV("DELETING WEAK REFERENCE %p (%p)", h.get(), p);
- if (p) delete p;
- }
- return NO_ERROR;
+ // change logging level
+ int n = args.size();
+ for (int i = 0; i + 1 < n; i++) {
+ if (args[i] == String16("-v")) {
+ String8 levelStr(args[i+1]);
+ int level = atoi(levelStr.string());
+ sprintf(buffer, "Set Log Level to %d", level);
+ result.append(buffer);
+ setLogLevel(level);
}
}
- break;
- default:
- break;
- }
}
-#endif // DEBUG_HEAP_LEAKS
-
- return err;
+ return NO_ERROR;
}
}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 75e96c6..0d69836 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -18,209 +18,182 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
+#include <binder/BinderService.h>
+
#include <camera/ICameraService.h>
#include <camera/CameraHardwareInterface.h>
-#include <camera/Camera.h>
+
+/* This needs to be increased if we can have more cameras */
+#define MAX_CAMERAS 2
namespace android {
class MemoryHeapBase;
class MediaPlayer;
-// ----------------------------------------------------------------------------
-
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
-
-// When enabled, this feature allows you to send an event to the CameraService
-// so that you can cause all references to the heap object gWeakHeap, defined
-// below, to be printed. You will also need to set DEBUG_REFS=1 and
-// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp. You just have to
-// set gWeakHeap to the appropriate heap you want to track.
-
-#define DEBUG_HEAP_LEAKS 0
-
-// ----------------------------------------------------------------------------
-
-class CameraService : public BnCameraService
+class CameraService :
+ public BinderService<CameraService>,
+ public BnCameraService
{
class Client;
-
+ friend class BinderService<CameraService>;
public:
- static void instantiate();
-
- // ICameraService interface
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- void removeClient(const sp<ICameraClient>& cameraClient);
+ static char const* getServiceName() { return "media.camera"; }
+
+ CameraService();
+ virtual ~CameraService();
+
+ virtual int32_t getNumberOfCameras();
+ virtual status_t getCameraInfo(int cameraId,
+ struct CameraInfo* cameraInfo);
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId);
+ virtual void removeClient(const sp<ICameraClient>& cameraClient);
+ virtual sp<Client> getClientById(int cameraId);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+ enum sound_kind {
+ SOUND_SHUTTER = 0,
+ SOUND_RECORDING = 1,
+ NUM_SOUNDS
+ };
- virtual status_t onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+ void loadSound();
+ void playSound(sound_kind kind);
+ void releaseSound();
private:
-
-// ----------------------------------------------------------------------------
-
- class Client : public BnCamera {
-
+ Mutex mServiceLock;
+ wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock
+ int mNumberOfCameras;
+
+ // atomics to record whether the hardware is allocated to some client.
+ volatile int32_t mBusy[MAX_CAMERAS];
+ void setCameraBusy(int cameraId);
+ void setCameraFree(int cameraId);
+
+ // sounds
+ Mutex mSoundLock;
+ sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS];
+ int mSoundRef; // reference count (release all MediaPlayer when 0)
+
+ class Client : public BnCamera
+ {
public:
+ // ICamera interface (see ICamera for details)
virtual void disconnect();
-
- // connect new client with existing camera remote
virtual status_t connect(const sp<ICameraClient>& client);
-
- // prevent other processes from using this ICamera interface
virtual status_t lock();
-
- // allow other processes to use this ICamera interface
virtual status_t unlock();
-
- // pass the buffered ISurface to the camera service
virtual status_t setPreviewDisplay(const sp<ISurface>& surface);
-
- // set the preview callback flag to affect how the received frames from
- // preview are handled.
- virtual void setPreviewCallbackFlag(int callback_flag);
-
- // start preview mode, must call setPreviewDisplay first
+ virtual void setPreviewCallbackFlag(int flag);
virtual status_t startPreview();
-
- // stop preview mode
virtual void stopPreview();
-
- // get preview state
virtual bool previewEnabled();
-
- // start recording mode
virtual status_t startRecording();
-
- // stop recording mode
virtual void stopRecording();
-
- // get recording state
virtual bool recordingEnabled();
-
- // release a recording frame
virtual void releaseRecordingFrame(const sp<IMemory>& mem);
-
- // auto focus
virtual status_t autoFocus();
-
- // cancel auto focus
virtual status_t cancelAutoFocus();
-
- // take a picture - returns an IMemory (ref-counted mmap)
virtual status_t takePicture();
-
- // set preview/capture parameters - key/value pairs
virtual status_t setParameters(const String8& params);
-
- // get preview/capture parameters - key/value pairs
virtual String8 getParameters() const;
-
- // send command to camera driver
virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
-
- // our client...
- const sp<ICameraClient>& getCameraClient() const { return mCameraClient; }
-
private:
friend class CameraService;
Client(const sp<CameraService>& cameraService,
- const sp<ICameraClient>& cameraClient,
- pid_t clientPid);
- Client();
- virtual ~Client();
-
- status_t checkPid();
-
- static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
- static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user);
- static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
- const sp<IMemory>& dataPtr, void* user);
+ const sp<ICameraClient>& cameraClient,
+ const sp<CameraHardwareInterface>& hardware,
+ int cameraId,
+ int clientPid);
+ ~Client();
- static sp<Client> getClientFromCookie(void* user);
+ // return our camera client
+ const sp<ICameraClient>& getCameraClient() { return mCameraClient; }
- void handlePreviewData(const sp<IMemory>&);
- void handleShutter(image_rect_type *image);
- void handlePostview(const sp<IMemory>&);
- void handleRawPicture(const sp<IMemory>&);
- void handleCompressedPicture(const sp<IMemory>&);
+ // check whether the calling process matches mClientPid.
+ status_t checkPid() const;
+ status_t checkPidAndHardware() const; // also check mHardware != 0
- void copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client,
- const sp<IMemoryHeap>& heap, size_t offset, size_t size);
+ // these are internal functions used to set up preview buffers
+ status_t registerPreviewBuffers();
+ status_t setOverlay();
// camera operation mode
enum camera_mode {
CAMERA_PREVIEW_MODE = 0, // frame automatically released
CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame()
};
+ // these are internal functions used for preview/recording
status_t startCameraMode(camera_mode mode);
status_t startPreviewMode();
status_t startRecordingMode();
- status_t setOverlay();
- status_t registerPreviewBuffers();
+
+ // these are static callback functions
+ static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
+ static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user);
+ static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user);
+ // convert client from cookie
+ static sp<Client> getClientFromCookie(void* user);
+ // handlers for messages
+ void handleShutter(image_rect_type *size);
+ void handlePreviewData(const sp<IMemory>& mem);
+ void handlePostview(const sp<IMemory>& mem);
+ void handleRawPicture(const sp<IMemory>& mem);
+ void handleCompressedPicture(const sp<IMemory>& mem);
+ void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2);
+ void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr);
+ void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
+
+ void copyFrameAndPostCopiedFrame(
+ const sp<ICameraClient>& client,
+ const sp<IMemoryHeap>& heap,
+ size_t offset, size_t size);
+
+ // these are initialized in the constructor.
+ sp<CameraService> mCameraService; // immutable after constructor
+ sp<ICameraClient> mCameraClient;
+ int mCameraId; // immutable after constructor
+ pid_t mClientPid;
+ sp<CameraHardwareInterface> mHardware; // cleared after disconnect()
+ bool mUseOverlay; // immutable after constructor
+ sp<OverlayRef> mOverlayRef;
+ int mOverlayW;
+ int mOverlayH;
+ int mPreviewCallbackFlag;
+ int mOrientation;
// Ensures atomicity among the public methods
- mutable Mutex mLock;
-
- // mSurfaceLock synchronizes access to mSurface between
- // setPreviewSurface() and postPreviewFrame(). Note that among
- // the public methods, all accesses to mSurface are
- // syncrhonized by mLock. However, postPreviewFrame() is called
- // by the CameraHardwareInterface callback, and needs to
- // access mSurface. It cannot hold mLock, however, because
- // stopPreview() may be holding that lock while attempting
- // to stop preview, and stopPreview itself will block waiting
- // for a callback from CameraHardwareInterface. If this
- // happens, it will cause a deadlock.
- mutable Mutex mSurfaceLock;
- mutable Condition mReady;
- sp<CameraService> mCameraService;
- sp<ISurface> mSurface;
- int mPreviewCallbackFlag;
- int mOrientation;
-
- sp<MediaPlayer> mMediaPlayerClick;
- sp<MediaPlayer> mMediaPlayerBeep;
-
- // these are immutable once the object is created,
- // they don't need to be protected by a lock
- sp<ICameraClient> mCameraClient;
- sp<CameraHardwareInterface> mHardware;
- pid_t mClientPid;
- bool mUseOverlay;
-
- sp<OverlayRef> mOverlayRef;
- int mOverlayW;
- int mOverlayH;
-
- mutable Mutex mPreviewLock;
- sp<MemoryHeapBase> mPreviewBuffer;
+ mutable Mutex mLock;
+ sp<ISurface> mSurface;
+
+ // If the user want us to return a copy of the preview frame (instead
+ // of the original one), we allocate mPreviewBuffer and reuse it if possible.
+ sp<MemoryHeapBase> mPreviewBuffer;
+
+ // We need to avoid the deadlock when the incoming command thread and
+ // the CameraHardwareInterface callback thread both want to grab mLock.
+ // An extra flag is used to tell the callback thread that it should stop
+ // trying to deliver the callback messages if the client is not
+ // interested in it anymore. For example, if the client is calling
+ // stopPreview(), the preview frame messages do not need to be delivered
+ // anymore.
+
+ // This function takes the same parameter as the enableMsgType() and
+ // disableMsgType() functions in CameraHardwareInterface.
+ void enableMsgType(int32_t msgType);
+ void disableMsgType(int32_t msgType);
+ volatile int32_t mMsgEnabled;
+
+ // This function keeps trying to grab mLock, or give up if the message
+ // is found to be disabled. It returns true if mLock is grabbed.
+ bool lockIfMessageWanted(int32_t msgType);
};
-
-// ----------------------------------------------------------------------------
-
- CameraService();
- virtual ~CameraService();
-
- // We use a count for number of clients (shoule only be 0 or 1).
- volatile int32_t mUsers;
- virtual void incUsers();
- virtual void decUsers();
-
- mutable Mutex mServiceLock;
- wp<Client> mClient;
-
-#if DEBUG_HEAP_LEAKS
- wp<IMemoryHeap> gWeakHeap;
-#endif
};
-// ----------------------------------------------------------------------------
-
-}; // namespace android
+} // namespace android
#endif
diff --git a/services/camera/libcameraservice/FakeCamera.cpp b/services/camera/libcameraservice/FakeCamera.cpp
index 6749899..f3a6a67 100644
--- a/services/camera/libcameraservice/FakeCamera.cpp
+++ b/services/camera/libcameraservice/FakeCamera.cpp
@@ -198,10 +198,11 @@ static const int SHIFT2 = 16;
static const int DELTA = kYb*(1 << SHIFT2);
static const int GAMMA = kYr*(1 << SHIFT2);
-int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[])
+int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16, uint8_t *yuv420,
+ uint32_t *param, uint8_t *table[])
{
uint16_t *inputRGB = (uint16_t*)rgb16;
- uint8_t *outYUV = yuv422;
+ uint8_t *outYUV = yuv420;
int32_t width_dst = param[0];
int32_t height_dst = param[1];
int32_t pitch_dst = param[2];
@@ -260,12 +261,14 @@ uint32_t temp;
tempY[0] = y0;
tempY[1] = y1;
- tempU[0] = u;
- tempV[0] = v;
-
tempY += 2;
- tempU += 2;
- tempV += 2;
+
+ if ((j&1) == 0) {
+ tempU[0] = u;
+ tempV[0] = v;
+ tempU += 2;
+ tempV += 2;
+ }
}
inputRGB += pitch_src;
@@ -277,7 +280,7 @@ uint32_t temp;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
-static void convert_rgb16_to_yuv422(uint8_t *rgb, uint8_t *yuv, int width, int height)
+static void convert_rgb16_to_yuv420(uint8_t *rgb, uint8_t *yuv, int width, int height)
{
if (!tables_initialized) {
initYtab();
@@ -326,7 +329,7 @@ void FakeCamera::setSize(int width, int height)
mCheckY = 0;
// This will cause it to be reallocated on the next call
- // to getNextFrameAsYuv422().
+ // to getNextFrameAsYuv420().
delete[] mTmpRgb16Buffer;
mTmpRgb16Buffer = 0;
}
@@ -347,13 +350,13 @@ void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer)
mCounter++;
}
-void FakeCamera::getNextFrameAsYuv422(uint8_t *buffer)
+void FakeCamera::getNextFrameAsYuv420(uint8_t *buffer)
{
if (mTmpRgb16Buffer == 0)
mTmpRgb16Buffer = new uint16_t[mWidth * mHeight];
getNextFrameAsRgb565(mTmpRgb16Buffer);
- convert_rgb16_to_yuv422((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight);
+ convert_rgb16_to_yuv420((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight);
}
void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow)
diff --git a/services/camera/libcameraservice/FakeCamera.h b/services/camera/libcameraservice/FakeCamera.h
index f7f8803..724de20 100644
--- a/services/camera/libcameraservice/FakeCamera.h
+++ b/services/camera/libcameraservice/FakeCamera.h
@@ -40,7 +40,7 @@ public:
~FakeCamera();
void setSize(int width, int height);
- void getNextFrameAsYuv422(uint8_t *buffer);
+ void getNextFrameAsYuv420(uint8_t *buffer);
// Write to the fd a string representing the current state.
void dump(int fd) const;
diff --git a/services/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk
index 9bb190a..cf4e42f 100644
--- a/services/camera/tests/CameraServiceTest/Android.mk
+++ b/services/camera/tests/CameraServiceTest/Android.mk
@@ -21,4 +21,6 @@ LOCAL_SHARED_LIBRARIES += \
libcamera_client \
libsurfaceflinger_client
-include $(BUILD_EXECUTABLE)
+# Disable it because the ISurface interface may change, and before we have a
+# chance to fix this test, we don't want to break normal builds.
+#include $(BUILD_EXECUTABLE)
diff --git a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
index 9fc795b..3c8d553 100644
--- a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
+++ b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
@@ -38,7 +38,7 @@ void assert_fail(const char *file, int line, const char *func, const char *expr)
INFO("assertion failed at file %s, line %d, function %s:",
file, line, func);
INFO("%s", expr);
- exit(1);
+ abort();
}
void assert_eq_fail(const char *file, int line, const char *func,
@@ -46,7 +46,7 @@ void assert_eq_fail(const char *file, int line, const char *func,
INFO("assertion failed at file %s, line %d, function %s:",
file, line, func);
INFO("(expected) %s != (actual) %d", expr, actual);
- exit(1);
+ abort();
}
#define ASSERT(e) \
@@ -155,7 +155,7 @@ public:
virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
virtual void dataCallback(int32_t msgType, const sp<IMemory>& data);
virtual void dataCallbackTimestamp(nsecs_t timestamp,
- int32_t msgType, const sp<IMemory>& data) {}
+ int32_t msgType, const sp<IMemory>& data);
// new functions
void clearStat();
@@ -176,6 +176,7 @@ private:
DefaultKeyedVector<int32_t, int> mDataCount;
DefaultKeyedVector<int32_t, int> mDataSize;
bool test(OP op, int v1, int v2);
+ void assertTest(OP op, int v1, int v2);
ICamera *mReleaser;
};
@@ -199,26 +200,33 @@ bool MCameraClient::test(OP op, int v1, int v2) {
return false;
}
+void MCameraClient::assertTest(OP op, int v1, int v2) {
+ if (!test(op, v1, v2)) {
+ LOGE("assertTest failed: op=%d, v1=%d, v2=%d", op, v1, v2);
+ ASSERT(0);
+ }
+}
+
void MCameraClient::assertNotify(int32_t msgType, OP op, int count) {
Mutex::Autolock _l(mLock);
int v = mNotifyCount.valueFor(msgType);
- ASSERT(test(op, v, count));
+ assertTest(op, v, count);
}
void MCameraClient::assertData(int32_t msgType, OP op, int count) {
Mutex::Autolock _l(mLock);
int v = mDataCount.valueFor(msgType);
- ASSERT(test(op, v, count));
+ assertTest(op, v, count);
}
void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) {
Mutex::Autolock _l(mLock);
int v = mDataSize.valueFor(msgType);
- ASSERT(test(op, v, dataSize));
+ assertTest(op, v, dataSize);
}
void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
- INFO(__func__);
+ INFO("%s", __func__);
Mutex::Autolock _l(mLock);
ssize_t i = mNotifyCount.indexOfKey(msgType);
if (i < 0) {
@@ -230,7 +238,7 @@ void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
}
void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) {
- INFO(__func__);
+ INFO("%s", __func__);
int dataSize = data->size();
INFO("data type = %d, size = %d", msgType, dataSize);
Mutex::Autolock _l(mLock);
@@ -250,6 +258,11 @@ void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) {
}
}
+void MCameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+ const sp<IMemory>& data) {
+ dataCallback(msgType, data);
+}
+
void MCameraClient::waitNotify(int32_t msgType, OP op, int count) {
INFO("waitNotify: %d, %d, %d", msgType, op, count);
Mutex::Autolock _l(mLock);
@@ -285,6 +298,7 @@ public:
virtual sp<OverlayRef> createOverlay(
uint32_t w, uint32_t h, int32_t format, int32_t orientation);
virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage);
+ virtual status_t setBufferCount(int bufferCount);
// new functions
void clearStat();
@@ -300,7 +314,7 @@ private:
};
status_t MSurface::registerBuffers(const BufferHeap& buffers) {
- INFO(__func__);
+ INFO("%s", __func__);
Mutex::Autolock _l(mLock);
++registerBuffersCount;
mCond.signal();
@@ -308,21 +322,26 @@ status_t MSurface::registerBuffers(const BufferHeap& buffers) {
}
void MSurface::postBuffer(ssize_t offset) {
- // INFO(__func__);
+ // INFO("%s", __func__);
Mutex::Autolock _l(mLock);
++postBufferCount;
mCond.signal();
}
void MSurface::unregisterBuffers() {
- INFO(__func__);
+ INFO("%s", __func__);
Mutex::Autolock _l(mLock);
++unregisterBuffersCount;
mCond.signal();
}
sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) {
- INFO(__func__);
+ INFO("%s", __func__);
+ return NULL;
+}
+
+status_t MSurface::setBufferCount(int bufferCount) {
+ INFO("%s", __func__);
return NULL;
}
@@ -348,10 +367,9 @@ void MSurface::waitUntil(int c0, int c1, int c2) {
sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format,
int32_t orientation) {
- // We don't expect this to be called in current hardware.
+ // Not implemented.
ASSERT(0);
- sp<OverlayRef> dummy;
- return dummy;
+ return NULL;
}
//
@@ -368,17 +386,17 @@ sp<IHolder> getHolder() {
}
void putTempObject(sp<IBinder> obj) {
- INFO(__func__);
+ INFO("%s", __func__);
getHolder()->put(obj);
}
sp<IBinder> getTempObject() {
- INFO(__func__);
+ INFO("%s", __func__);
return getHolder()->get();
}
void clearTempObject() {
- INFO(__func__);
+ INFO("%s", __func__);
getHolder()->clear();
}
@@ -395,64 +413,71 @@ sp<ICameraService> getCameraService() {
return cs;
}
+int getNumberOfCameras() {
+ sp<ICameraService> cs = getCameraService();
+ return cs->getNumberOfCameras();
+}
+
//
// Various Connect Tests
//
-void testConnect() {
- INFO(__func__);
+void testConnect(int cameraId) {
+ INFO("%s", __func__);
sp<ICameraService> cs = getCameraService();
sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
+ sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
c->disconnect();
}
-void testAllowConnectOnceOnly() {
- INFO(__func__);
+void testAllowConnectOnceOnly(int cameraId) {
+ INFO("%s", __func__);
sp<ICameraService> cs = getCameraService();
// Connect the first client.
sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
+ sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
// Same client -- ok.
- ASSERT(cs->connect(cc) != 0);
+ ASSERT(cs->connect(cc, cameraId) != 0);
// Different client -- not ok.
sp<MCameraClient> cc2 = new MCameraClient();
- ASSERT(cs->connect(cc2) == 0);
+ ASSERT(cs->connect(cc2, cameraId) == 0);
c->disconnect();
}
void testReconnectFailed() {
- INFO(__func__);
+ INFO("%s", __func__);
sp<ICamera> c = interface_cast<ICamera>(getTempObject());
- sp<MCameraClient> cc2 = new MCameraClient();
- ASSERT(c->connect(cc2) != NO_ERROR);
+ sp<MCameraClient> cc = new MCameraClient();
+ ASSERT(c->connect(cc) != NO_ERROR);
}
void testReconnectSuccess() {
- INFO(__func__);
+ INFO("%s", __func__);
sp<ICamera> c = interface_cast<ICamera>(getTempObject());
sp<MCameraClient> cc = new MCameraClient();
ASSERT(c->connect(cc) == NO_ERROR);
+ c->disconnect();
}
void testLockFailed() {
- INFO(__func__);
+ INFO("%s", __func__);
sp<ICamera> c = interface_cast<ICamera>(getTempObject());
ASSERT(c->lock() != NO_ERROR);
}
void testLockUnlockSuccess() {
- INFO(__func__);
+ INFO("%s", __func__);
sp<ICamera> c = interface_cast<ICamera>(getTempObject());
ASSERT(c->lock() == NO_ERROR);
ASSERT(c->unlock() == NO_ERROR);
}
void testLockSuccess() {
- INFO(__func__);
+ INFO("%s", __func__);
sp<ICamera> c = interface_cast<ICamera>(getTempObject());
ASSERT(c->lock() == NO_ERROR);
+ c->disconnect();
}
//
@@ -499,11 +524,11 @@ void runInAnotherProcess(const char *tag) {
}
}
-void testReconnect() {
- INFO(__func__);
+void testReconnect(int cameraId) {
+ INFO("%s", __func__);
sp<ICameraService> cs = getCameraService();
sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
+ sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
// Reconnect to the same client -- ok.
ASSERT(c->connect(cc) == NO_ERROR);
@@ -514,10 +539,10 @@ void testReconnect() {
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
}
-void testLockUnlock() {
+void testLockUnlock(int cameraId) {
sp<ICameraService> cs = getCameraService();
sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
+ sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
// We can lock as many times as we want.
ASSERT(c->lock() == NO_ERROR);
@@ -530,16 +555,15 @@ void testLockUnlock() {
runInAnotherProcess("testLockUnlockSuccess");
// Unlock then lock from a different process -- ok.
runInAnotherProcess("testLockSuccess");
- c->disconnect();
clearTempObject();
}
-void testReconnectFromAnotherProcess() {
- INFO(__func__);
+void testReconnectFromAnotherProcess(int cameraId) {
+ INFO("%s", __func__);
sp<ICameraService> cs = getCameraService();
sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
+ sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
// Reconnect from a different process -- not ok.
putTempObject(c->asBinder());
@@ -547,7 +571,6 @@ void testReconnectFromAnotherProcess() {
// Unlock then reconnect from a different process -- ok.
ASSERT(c->unlock() == NO_ERROR);
runInAnotherProcess("testReconnectSuccess");
- c->disconnect();
clearTempObject();
}
@@ -560,10 +583,11 @@ static void flushCommands() {
}
// Run a test case
-#define RUN(class_name) do { \
+#define RUN(class_name, cameraId) do { \
{ \
INFO(#class_name); \
class_name instance; \
+ instance.init(cameraId); \
instance.run(); \
} \
flushCommands(); \
@@ -571,19 +595,21 @@ static void flushCommands() {
// Base test case after the the camera is connected.
class AfterConnect {
-protected:
- sp<ICameraService> cs;
- sp<MCameraClient> cc;
- sp<ICamera> c;
-
- AfterConnect() {
+public:
+ void init(int cameraId) {
cs = getCameraService();
cc = new MCameraClient();
- c = cs->connect(cc);
+ c = cs->connect(cc, cameraId);
ASSERT(c != 0);
}
+protected:
+ sp<ICameraService> cs;
+ sp<MCameraClient> cc;
+ sp<ICamera> c;
+
~AfterConnect() {
+ c->disconnect();
c.clear();
cc.clear();
cs.clear();
@@ -612,19 +638,16 @@ public:
surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer
surface->clearStat();
- c->disconnect();
- // TODO: CameraService crashes for this. Fix it.
-#if 0
sp<MSurface> another_surface = new MSurface();
c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers
// is called.
surface->waitUntil(0, 0, 1); // needs unregisterBuffers
-#endif
+
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
}
};
-class TestStartPreviewWithoutDisplay : AfterConnect {
+class TestStartPreviewWithoutDisplay : public AfterConnect {
public:
void run() {
ASSERT(c->startPreview() == NO_ERROR);
@@ -636,15 +659,17 @@ public:
// Base test case after the the camera is connected and the preview is started.
class AfterStartPreview : public AfterConnect {
-protected:
- sp<MSurface> surface;
-
- AfterStartPreview() {
+public:
+ void init(int cameraId) {
+ AfterConnect::init(cameraId);
surface = new MSurface();
ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
ASSERT(c->startPreview() == NO_ERROR);
}
+protected:
+ sp<MSurface> surface;
+
~AfterStartPreview() {
surface.clear();
}
@@ -680,9 +705,6 @@ public:
cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
c->stopPreview();
-#if 1 // TODO: It crashes if we don't have this. Fix it.
- usleep(100000);
-#endif
c->disconnect();
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
}
@@ -697,7 +719,6 @@ public:
cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1);
cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
- usleep(100000); // 100ms
}
c->disconnect();
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
@@ -708,36 +729,71 @@ class TestGetParameters: public AfterStartPreview {
public:
void run() {
String8 param_str = c->getParameters();
- INFO(param_str);
+ INFO("%s", static_cast<const char*>(param_str));
}
};
+static bool getNextSize(const char **ptrS, int *w, int *h) {
+ const char *s = *ptrS;
+
+ // skip over ','
+ if (*s == ',') s++;
+
+ // remember start position in p
+ const char *p = s;
+ while (*s != '\0' && *s != 'x') {
+ s++;
+ }
+ if (*s == '\0') return false;
+
+ // get the width
+ *w = atoi(p);
+
+ // skip over 'x'
+ ASSERT(*s == 'x');
+ p = s + 1;
+ while (*s != '\0' && *s != ',') {
+ s++;
+ }
+
+ // get the height
+ *h = atoi(p);
+ *ptrS = s;
+ return true;
+}
+
class TestPictureSize : public AfterStartPreview {
public:
void checkOnePicture(int w, int h) {
- const float rate = 0.5; // byte per pixel limit
+ const float rate = 0.9; // byte per pixel limit
int pixels = w * h;
CameraParameters param(c->getParameters());
param.setPictureSize(w, h);
+ // disable thumbnail to get more accurate size.
+ param.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0);
+ param.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0);
c->setParameters(param.flatten());
cc->clearStat();
ASSERT(c->takePicture() == NO_ERROR);
cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
- cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2);
+ //cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2);
cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT,
int(pixels * rate));
cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0);
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- usleep(100000); // 100ms
}
void run() {
- checkOnePicture(2048, 1536);
- checkOnePicture(1600, 1200);
- checkOnePicture(1024, 768);
+ CameraParameters param(c->getParameters());
+ int w, h;
+ const char *s = param.get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES);
+ while (getNextSize(&s, &w, &h)) {
+ LOGD("checking picture size %dx%d", w, h);
+ checkOnePicture(w, h);
+ }
}
};
@@ -749,6 +805,8 @@ public:
// Try all flag combinations.
for (int v = 0; v < 8; v++) {
+ LOGD("TestPreviewCallbackFlag: flag=%d", v);
+ usleep(100000); // sleep a while to clear the in-flight callbacks.
cc->clearStat();
c->setPreviewCallbackFlag(v);
ASSERT(c->previewEnabled() == false);
@@ -781,6 +839,7 @@ public:
ASSERT(c->recordingEnabled() == true);
sleep(2);
c->stopRecording();
+ usleep(100000); // sleep a while to clear the in-flight callbacks.
cc->setReleaser(NULL);
cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10);
}
@@ -806,9 +865,13 @@ public:
}
void run() {
- checkOnePicture(480, 320);
- checkOnePicture(352, 288);
- checkOnePicture(176, 144);
+ CameraParameters param(c->getParameters());
+ int w, h;
+ const char *s = param.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES);
+ while (getNextSize(&s, &w, &h)) {
+ LOGD("checking preview size %dx%d", w, h);
+ checkOnePicture(w, h);
+ }
}
};
@@ -827,23 +890,30 @@ int main(int argc, char **argv)
INFO("CameraServiceTest start");
gExecutable = argv[0];
runHolderService();
-
- testConnect(); flushCommands();
- testAllowConnectOnceOnly(); flushCommands();
- testReconnect(); flushCommands();
- testLockUnlock(); flushCommands();
- testReconnectFromAnotherProcess(); flushCommands();
-
- RUN(TestSetPreviewDisplay);
- RUN(TestStartPreview);
- RUN(TestStartPreviewWithoutDisplay);
- RUN(TestAutoFocus);
- RUN(TestStopPreview);
- RUN(TestTakePicture);
- RUN(TestTakeMultiplePictures);
- RUN(TestGetParameters);
- RUN(TestPictureSize);
- RUN(TestPreviewCallbackFlag);
- RUN(TestRecording);
- RUN(TestPreviewSize);
+ int n = getNumberOfCameras();
+ INFO("%d Cameras available", n);
+
+ for (int id = 0; id < n; id++) {
+ INFO("Testing camera %d", id);
+ testConnect(id); flushCommands();
+ testAllowConnectOnceOnly(id); flushCommands();
+ testReconnect(id); flushCommands();
+ testLockUnlock(id); flushCommands();
+ testReconnectFromAnotherProcess(id); flushCommands();
+
+ RUN(TestSetPreviewDisplay, id);
+ RUN(TestStartPreview, id);
+ RUN(TestStartPreviewWithoutDisplay, id);
+ RUN(TestAutoFocus, id);
+ RUN(TestStopPreview, id);
+ RUN(TestTakePicture, id);
+ RUN(TestTakeMultiplePictures, id);
+ RUN(TestGetParameters, id);
+ RUN(TestPictureSize, id);
+ RUN(TestPreviewCallbackFlag, id);
+ RUN(TestRecording, id);
+ RUN(TestPreviewSize, id);
+ }
+
+ INFO("CameraServiceTest finished");
}
diff --git a/services/java/Android.mk b/services/java/Android.mk
index 934712c..c756d29 100644
--- a/services/java/Android.mk
+++ b/services/java/Android.mk
@@ -13,7 +13,9 @@ LOCAL_MODULE:= services
LOCAL_JAVA_LIBRARIES := android.policy
+LOCAL_NO_EMMA_INSTRUMENT := true
+LOCAL_NO_EMMA_COMPILE := true
+
include $(BUILD_JAVA_LIBRARY)
include $(BUILD_DROIDDOC)
-
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index dc5fd30..731fb22 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
@@ -1099,7 +1100,7 @@ class AppWidgetService extends IAppWidgetService.Stub
if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
added = true;
- } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
added = false;
} else {
@@ -1160,7 +1161,9 @@ class AppWidgetService extends IAppWidgetService.Stub
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);
}
@@ -1179,6 +1182,9 @@ class AppWidgetService extends IAppWidgetService.Stub
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);
@@ -1204,6 +1210,7 @@ class AppWidgetService extends IAppWidgetService.Stub
// 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);
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6e307a5..3db5dc1 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -17,8 +17,8 @@
package com.android.server;
import android.app.ActivityManagerNative;
-import android.app.ActivityThread;
import android.app.AlarmManager;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IBackupAgent;
@@ -399,7 +399,7 @@ class BackupManagerService extends IBackupManager.Stub {
public BackupManagerService(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
- mPackageManagerBinder = ActivityThread.getPackageManager();
+ mPackageManagerBinder = AppGlobals.getPackageManager();
mActivityManager = ActivityManagerNative.getDefault();
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -421,7 +421,7 @@ class BackupManagerService extends IBackupManager.Stub {
Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
// If Encrypted file systems is enabled or disabled, this call will return the
// correct directory.
- mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
+ mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
mBaseStateDir.mkdirs();
mDataDir = Environment.getDownloadCacheDirectory();
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 5cf61bd..e6c32d9 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -210,9 +210,6 @@ class BatteryService extends Binder {
boolean logOutlier = false;
long dischargeDuration = 0;
- shutdownIfNoPower();
- shutdownIfOverTemp();
-
mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
if (mAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
@@ -221,6 +218,19 @@ class BatteryService extends Binder {
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}
+
+ // Let the battery stats keep track of the current level.
+ try {
+ mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
+ mPlugType, mBatteryLevel, mBatteryTemperature,
+ mBatteryVoltage);
+ } catch (RemoteException e) {
+ // Should never happen.
+ }
+
+ shutdownIfNoPower();
+ shutdownIfOverTemp();
+
if (mBatteryStatus != mLastBatteryStatus ||
mBatteryHealth != mLastBatteryHealth ||
mBatteryPresent != mLastBatteryPresent ||
@@ -263,16 +273,6 @@ class BatteryService extends Binder {
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
}
- if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) {
- // If the battery level has changed and we are on battery, update the current level.
- // This is used for discharge cycle tracking so this shouldn't be updated while the
- // battery is charging.
- try {
- mBatteryStats.recordCurrentLevel(mBatteryLevel);
- } catch (RemoteException e) {
- // Should never happen.
- }
- }
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
@@ -342,11 +342,6 @@ class BatteryService extends Binder {
Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- try {
- mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel);
- } catch (RemoteException e) {
- // Should never happen.
- }
int icon = getIcon(mBatteryLevel);
diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java
index f409751..b9ff8d0 100644
--- a/services/java/com/android/server/BootReceiver.java
+++ b/services/java/com/android/server/BootReceiver.java
@@ -165,7 +165,9 @@ public class BootReceiver extends BroadcastReceiver {
if (prefs != null) {
long lastTime = prefs.getLong(filename, 0);
if (lastTime == fileTime) return; // Already logged this particular file
- prefs.edit().putLong(filename, fileTime).commit();
+ // TODO: move all these SharedPreferences Editor commits
+ // outside this function to the end of logBootEvents
+ prefs.edit().putLong(filename, fileTime).apply();
}
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 9edce20..4c81380 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -585,7 +585,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
!network.isTeardownRequested()) {
if (ni.isConnected() == true) {
// add the pid-specific dns
- handleDnsConfigurationChange();
+ handleDnsConfigurationChange(networkType);
if (DBG) Slog.d(TAG, "special network already active");
return Phone.APN_ALREADY_ACTIVE;
}
@@ -920,7 +920,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -945,7 +944,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
// do this before we broadcast the change
- handleConnectivityChange();
+ handleConnectivityChange(prevNetType);
sendStickyBroadcast(intent);
/*
@@ -1036,7 +1035,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void sendConnectedBroadcast(NetworkInfo info) {
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -1074,7 +1072,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
@@ -1101,9 +1098,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- // do this before we broadcast the change
- handleConnectivityChange();
-
sendStickyBroadcast(intent);
/*
* If the failover network is already connected, then immediately send
@@ -1174,7 +1168,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
thisNet.setTeardownRequested(false);
thisNet.updateNetworkSettings();
- handleConnectivityChange();
+ handleConnectivityChange(type);
sendConnectedBroadcast(info);
}
@@ -1201,38 +1195,29 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
- * After any kind of change in the connectivity state of any network,
- * make sure that anything that depends on the connectivity state of
- * more than one network is set up correctly. We're mainly concerned
- * with making sure that the list of DNS servers is set up according
- * to which networks are connected, and ensuring that the right routing
- * table entries exist.
+ * After a change in the connectivity state of any network, We're mainly
+ * concerned with making sure that the list of DNS servers is setupup
+ * according to which networks are connected, and ensuring that the
+ * right routing table entries exist.
*/
- private void handleConnectivityChange() {
+ private void handleConnectivityChange(int netType) {
/*
* If a non-default network is enabled, add the host routes that
- * will allow it's DNS servers to be accessed. Only
- * If both mobile and wifi are enabled, add the host routes that
- * will allow MMS traffic to pass on the mobile network. But
- * remove the default route for the mobile network, so that there
- * will be only one default route, to ensure that all traffic
- * except MMS will travel via Wi-Fi.
+ * will allow it's DNS servers to be accessed.
*/
- handleDnsConfigurationChange();
+ handleDnsConfigurationChange(netType);
- for (int netType : mPriorityList) {
- if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
- if (mNetAttributes[netType].isDefault()) {
- mNetTrackers[netType].addDefaultRoute();
- } else {
- mNetTrackers[netType].addPrivateDnsRoutes();
- }
+ if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
+ if (mNetAttributes[netType].isDefault()) {
+ mNetTrackers[netType].addDefaultRoute();
} else {
- if (mNetAttributes[netType].isDefault()) {
- mNetTrackers[netType].removeDefaultRoute();
- } else {
- mNetTrackers[netType].removePrivateDnsRoutes();
- }
+ mNetTrackers[netType].addPrivateDnsRoutes();
+ }
+ } else {
+ if (mNetAttributes[netType].isDefault()) {
+ mNetTrackers[netType].removeDefaultRoute();
+ } else {
+ mNetTrackers[netType].removePrivateDnsRoutes();
}
}
}
@@ -1303,41 +1288,36 @@ public class ConnectivityService extends IConnectivityManager.Stub {
SystemProperties.set("net.dnschange", "" + (n+1));
}
- private void handleDnsConfigurationChange() {
+ private void handleDnsConfigurationChange(int netType) {
// add default net's dns entries
- for (int x = mPriorityList.length-1; x>= 0; x--) {
- int netType = mPriorityList[x];
- NetworkStateTracker nt = mNetTrackers[netType];
- if (nt != null && nt.getNetworkInfo().isConnected() &&
- !nt.isTeardownRequested()) {
- String[] dnsList = nt.getNameServers();
- if (mNetAttributes[netType].isDefault()) {
- int j = 1;
- for (String dns : dnsList) {
- if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
- if (DBG) {
- Slog.d(TAG, "adding dns " + dns + " for " +
- nt.getNetworkInfo().getTypeName());
- }
- SystemProperties.set("net.dns" + j++, dns);
+ NetworkStateTracker nt = mNetTrackers[netType];
+ if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
+ String[] dnsList = nt.getNameServers();
+ if (mNetAttributes[netType].isDefault()) {
+ int j = 1;
+ for (String dns : dnsList) {
+ if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+ if (DBG) {
+ Slog.d(TAG, "adding dns " + dns + " for " +
+ nt.getNetworkInfo().getTypeName());
}
+ SystemProperties.set("net.dns" + j++, dns);
}
- for (int k=j ; k<mNumDnsEntries; k++) {
- if (DBG) Slog.d(TAG, "erasing net.dns" + k);
- SystemProperties.set("net.dns" + k, "");
- }
- mNumDnsEntries = j;
- } else {
- // set per-pid dns for attached secondary nets
- List pids = mNetRequestersPids[netType];
- for (int y=0; y< pids.size(); y++) {
- Integer pid = (Integer)pids.get(y);
- writePidDns(dnsList, pid.intValue());
- }
+ }
+ for (int k=j ; k<mNumDnsEntries; k++) {
+ if (DBG) Slog.d(TAG, "erasing net.dns" + k);
+ SystemProperties.set("net.dns" + k, "");
+ }
+ mNumDnsEntries = j;
+ } else {
+ // set per-pid dns for attached secondary nets
+ List pids = mNetRequestersPids[netType];
+ for (int y=0; y< pids.size(); y++) {
+ Integer pid = (Integer)pids.get(y);
+ writePidDns(dnsList, pid.intValue());
}
}
}
-
bumpDns();
}
@@ -1468,9 +1448,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
handleNotificationChange(msg.arg1 == 1, msg.arg2,
(Notification) msg.obj);
+ break;
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
- handleDnsConfigurationChange();
+ info = (NetworkInfo) msg.obj;
+ type = info.getType();
+ handleDnsConfigurationChange(type);
break;
case NetworkStateTracker.EVENT_ROAMING_CHANGED:
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 0de11c6..14b7d3e 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -690,8 +690,6 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
// was lost. Tombstones are expunged by age (see above).
if (mAllFiles.blocks > mCachedQuotaBlocks) {
- Slog.i(TAG, "Usage (" + mAllFiles.blocks + ") > Quota (" + mCachedQuotaBlocks + ")");
-
// Find a fair share amount of space to limit each tag
int unsqueezed = mAllFiles.blocks, squeezed = 0;
TreeSet<FileList> tags = new TreeSet<FileList>(mFilesByTag.values());
diff --git a/services/java/com/android/server/status/StatusBarException.java b/services/java/com/android/server/InputApplication.java
index be58f59..38420d4 100644
--- a/services/java/com/android/server/status/StatusBarException.java
+++ b/services/java/com/android/server/InputApplication.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,10 +14,20 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.server;
-public class StatusBarException extends RuntimeException {
- StatusBarException(String msg) {
- super(msg);
- }
+/**
+ * Describes input-related application properties for use by the input dispatcher.
+ *
+ * @hide
+ */
+public final class InputApplication {
+ // Application name.
+ public String name;
+
+ // Dispatching timeout.
+ public long dispatchingTimeoutNanos;
+
+ // The application window token.
+ public Object token;
}
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
deleted file mode 100644
index 414b69f..0000000
--- a/services/java/com/android/server/InputDevice.java
+++ /dev/null
@@ -1,1025 +0,0 @@
-/*
- * Copyright (C) 2007 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.util.Slog;
-import android.view.Display;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.WindowManagerPolicy;
-
-import java.io.PrintWriter;
-
-public class InputDevice {
- static final boolean DEBUG_POINTERS = false;
- static final boolean DEBUG_HACKS = false;
-
- /** Amount that trackball needs to move in order to generate a key event. */
- static final int TRACKBALL_MOVEMENT_THRESHOLD = 6;
-
- /** Maximum number of pointers we will track and report. */
- static final int MAX_POINTERS = 10;
-
- /**
- * Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value.
- */
- private static final int JUMPY_EPSILON_DIVISOR = 212;
-
- /** Number of jumpy points to drop for touchscreens that need it. */
- private static final int JUMPY_TRANSITION_DROPS = 3;
- private static final int JUMPY_DROP_LIMIT = 3;
-
- final int id;
- final int classes;
- final String name;
- final AbsoluteInfo absX;
- final AbsoluteInfo absY;
- final AbsoluteInfo absPressure;
- final AbsoluteInfo absSize;
-
- long mKeyDownTime = 0;
- int mMetaKeysState = 0;
-
- // For use by KeyInputQueue for keeping track of the current touch
- // data in the old non-multi-touch protocol.
- final int[] curTouchVals = new int[MotionEvent.NUM_SAMPLE_DATA * 2];
-
- final MotionState mAbs = new MotionState(0, 0);
- final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD,
- TRACKBALL_MOVEMENT_THRESHOLD);
-
- static class MotionState {
- int xPrecision;
- int yPrecision;
- float xMoveScale;
- float yMoveScale;
- MotionEvent currentMove = null;
- boolean changed = false;
- boolean everChanged = false;
- long mDownTime = 0;
-
- // The currently assigned pointer IDs, corresponding to the last data.
- int[] mPointerIds = new int[MAX_POINTERS];
-
- // This is the last generated pointer data, ordered to match
- // mPointerIds.
- boolean mSkipLastPointers;
- int mLastNumPointers = 0;
- final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // This is the next set of pointer data being generated. It is not
- // in any known order, and will be propagated in to mLastData
- // as part of mapping it to the appropriate pointer IDs.
- // Note that we have one extra sample of data here, to help clients
- // avoid doing bounds checking.
- int mNextNumPointers = 0;
- final int[] mNextData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
- + MotionEvent.NUM_SAMPLE_DATA];
-
- // Used to determine whether we dropped bad data, to avoid doing
- // it repeatedly.
- final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS];
-
- // Used to count the number of jumpy points dropped.
- private int mJumpyPointsDropped = 0;
-
- // Used to perform averaging of reported coordinates, to smooth
- // the data and filter out transients during a release.
- static final int HISTORY_SIZE = 5;
- int[] mHistoryDataStart = new int[MAX_POINTERS];
- int[] mHistoryDataEnd = new int[MAX_POINTERS];
- final int[] mHistoryData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
- * HISTORY_SIZE];
- final int[] mAveragedData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // Temporary data structures for doing the pointer ID mapping.
- final int[] mLast2Next = new int[MAX_POINTERS];
- final int[] mNext2Last = new int[MAX_POINTERS];
- final long[] mNext2LastDistance = new long[MAX_POINTERS];
-
- // Temporary data structure for generating the final motion data.
- final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
-
- // This is not used here, but can be used by callers for state tracking.
- int mAddingPointerOffset = 0;
- final boolean[] mDown = new boolean[MAX_POINTERS];
-
- void dumpIntArray(PrintWriter pw, int[] array) {
- pw.print("[");
- for (int i=0; i<array.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(array[i]);
- }
- pw.print("]");
- }
-
- void dumpBooleanArray(PrintWriter pw, boolean[] array) {
- pw.print("[");
- for (int i=0; i<array.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(array[i] ? "true" : "false");
- }
- pw.print("]");
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("xPrecision="); pw.print(xPrecision);
- pw.print(" yPrecision="); pw.println(yPrecision);
- pw.print(prefix); pw.print("xMoveScale="); pw.print(xMoveScale);
- pw.print(" yMoveScale="); pw.println(yMoveScale);
- if (currentMove != null) {
- pw.print(prefix); pw.print("currentMove="); pw.println(currentMove);
- }
- if (changed || mDownTime != 0) {
- pw.print(prefix); pw.print("changed="); pw.print(changed);
- pw.print(" mDownTime="); pw.println(mDownTime);
- }
- pw.print(prefix); pw.print("mPointerIds="); dumpIntArray(pw, mPointerIds);
- pw.println("");
- if (mSkipLastPointers || mLastNumPointers != 0) {
- pw.print(prefix); pw.print("mSkipLastPointers="); pw.print(mSkipLastPointers);
- pw.print(" mLastNumPointers="); pw.println(mLastNumPointers);
- pw.print(prefix); pw.print("mLastData="); dumpIntArray(pw, mLastData);
- pw.println("");
- }
- if (mNextNumPointers != 0) {
- pw.print(prefix); pw.print("mNextNumPointers="); pw.println(mNextNumPointers);
- pw.print(prefix); pw.print("mNextData="); dumpIntArray(pw, mNextData);
- pw.println("");
- }
- pw.print(prefix); pw.print("mDroppedBadPoint=");
- dumpBooleanArray(pw, mDroppedBadPoint); pw.println("");
- pw.print(prefix); pw.print("mAddingPointerOffset="); pw.println(mAddingPointerOffset);
- pw.print(prefix); pw.print("mDown=");
- dumpBooleanArray(pw, mDown); pw.println("");
- }
-
- MotionState(int mx, int my) {
- xPrecision = mx;
- yPrecision = my;
- xMoveScale = mx != 0 ? (1.0f/mx) : 1.0f;
- yMoveScale = my != 0 ? (1.0f/my) : 1.0f;
- for (int i=0; i<MAX_POINTERS; i++) {
- mPointerIds[i] = i;
- }
- }
-
- /**
- * Special hack for devices that have bad screen data: if one of the
- * points has moved more than a screen height from the last position,
- * then drop it.
- */
- void dropBadPoint(InputDevice dev) {
- // We should always have absY, but let's be paranoid.
- if (dev.absY == null) {
- return;
- }
- // Don't do anything if a finger is going down or up. We run
- // here before assigning pointer IDs, so there isn't a good
- // way to do per-finger matching.
- if (mNextNumPointers != mLastNumPointers) {
- return;
- }
-
- // We consider a single movement across more than a 7/16 of
- // the long size of the screen to be bad. This was a magic value
- // determined by looking at the maximum distance it is feasible
- // to actually move in one sample.
- final int maxDy = ((dev.absY.maxValue-dev.absY.minValue)*7)/16;
-
- // Look through all new points and see if any are farther than
- // acceptable from all previous points.
- for (int i=mNextNumPointers-1; i>=0; i--) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- //final int x = mNextData[ioff + MotionEvent.SAMPLE_X];
- final int y = mNextData[ioff + MotionEvent.SAMPLE_Y];
- if (DEBUG_HACKS) Slog.v("InputDevice", "Looking at next point #" + i + ": y=" + y);
- boolean dropped = false;
- if (!mDroppedBadPoint[i] && mLastNumPointers > 0) {
- dropped = true;
- int closestDy = -1;
- int closestY = -1;
- // We will drop this new point if it is sufficiently
- // far away from -all- last points.
- for (int j=mLastNumPointers-1; j>=0; j--) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- //int dx = x - mLastData[joff + MotionEvent.SAMPLE_X];
- int dy = y - mLastData[joff + MotionEvent.SAMPLE_Y];
- //if (dx < 0) dx = -dx;
- if (dy < 0) dy = -dy;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Comparing with last point #" + j
- + ": y=" + mLastData[joff] + " dy=" + dy);
- if (dy < maxDy) {
- dropped = false;
- break;
- } else if (closestDy < 0 || dy < closestDy) {
- closestDy = dy;
- closestY = mLastData[joff + MotionEvent.SAMPLE_Y];
- }
- }
- if (dropped) {
- dropped = true;
- Slog.i("InputDevice", "Dropping bad point #" + i
- + ": newY=" + y + " closestDy=" + closestDy
- + " maxDy=" + maxDy);
- mNextData[ioff + MotionEvent.SAMPLE_Y] = closestY;
- break;
- }
- }
- mDroppedBadPoint[i] = dropped;
- }
- }
-
- void dropJumpyPoint(InputDevice dev) {
- // We should always have absY, but let's be paranoid.
- if (dev.absY == null) {
- return;
- }
- final int jumpyEpsilon = dev.absY.range / JUMPY_EPSILON_DIVISOR;
-
- final int nextNumPointers = mNextNumPointers;
- final int lastNumPointers = mLastNumPointers;
- final int[] nextData = mNextData;
- final int[] lastData = mLastData;
-
- if (nextNumPointers != mLastNumPointers) {
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Different pointer count " + lastNumPointers +
- " -> " + nextNumPointers);
- for (int i = 0; i < nextNumPointers; i++) {
- int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- Slog.d("InputDevice", "Pointer " + i + " (" +
- mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
- mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
- }
- }
-
- // Just drop the first few events going from 1 to 2 pointers.
- // They're bad often enough that they're not worth considering.
- if (lastNumPointers == 1 && nextNumPointers == 2
- && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- mNextNumPointers = 1;
- mJumpyPointsDropped++;
- } else if (lastNumPointers == 2 && nextNumPointers == 1
- && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- // The event when we go from 2 -> 1 tends to be messed up too
- System.arraycopy(lastData, 0, nextData, 0,
- lastNumPointers * MotionEvent.NUM_SAMPLE_DATA);
- mNextNumPointers = lastNumPointers;
- mJumpyPointsDropped++;
-
- if (DEBUG_HACKS) {
- for (int i = 0; i < mNextNumPointers; i++) {
- int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- Slog.d("InputDevice", "Pointer " + i + " replaced (" +
- mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
- mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
- }
- }
- } else {
- mJumpyPointsDropped = 0;
-
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Transition - drop limit reset");
- }
- }
- return;
- }
-
- // A 'jumpy' point is one where the coordinate value for one axis
- // has jumped to the other pointer's location. No need to do anything
- // else if we only have one pointer.
- if (nextNumPointers < 2) {
- return;
- }
-
- int badPointerIndex = -1;
- int badPointerReplaceXWith = 0;
- int badPointerReplaceYWith = 0;
- int badPointerDistance = Integer.MIN_VALUE;
- for (int i = nextNumPointers - 1; i >= 0; i--) {
- boolean dropx = false;
- boolean dropy = false;
-
- // Limit how many times a jumpy point can get dropped.
- if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- final int x = nextData[ioff + MotionEvent.SAMPLE_X];
- final int y = nextData[ioff + MotionEvent.SAMPLE_Y];
-
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")");
- }
-
- // Check if a touch point is too close to another's coordinates
- for (int j = 0; j < nextNumPointers && !dropx && !dropy; j++) {
- if (j == i) {
- continue;
- }
-
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- final int xOther = nextData[joff + MotionEvent.SAMPLE_X];
- final int yOther = nextData[joff + MotionEvent.SAMPLE_Y];
-
- dropx = Math.abs(x - xOther) <= jumpyEpsilon;
- dropy = Math.abs(y - yOther) <= jumpyEpsilon;
- }
-
- if (dropx) {
- int xreplace = lastData[MotionEvent.SAMPLE_X];
- int yreplace = lastData[MotionEvent.SAMPLE_Y];
- int distance = Math.abs(yreplace - y);
- for (int j = 1; j < lastNumPointers; j++) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- int lasty = lastData[joff + MotionEvent.SAMPLE_Y];
- int currDist = Math.abs(lasty - y);
- if (currDist < distance) {
- xreplace = lastData[joff + MotionEvent.SAMPLE_X];
- yreplace = lasty;
- distance = currDist;
- }
- }
-
- int badXDelta = Math.abs(xreplace - x);
- if (badXDelta > badPointerDistance) {
- badPointerDistance = badXDelta;
- badPointerIndex = i;
- badPointerReplaceXWith = xreplace;
- badPointerReplaceYWith = yreplace;
- }
- } else if (dropy) {
- int xreplace = lastData[MotionEvent.SAMPLE_X];
- int yreplace = lastData[MotionEvent.SAMPLE_Y];
- int distance = Math.abs(xreplace - x);
- for (int j = 1; j < lastNumPointers; j++) {
- final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
- int lastx = lastData[joff + MotionEvent.SAMPLE_X];
- int currDist = Math.abs(lastx - x);
- if (currDist < distance) {
- xreplace = lastx;
- yreplace = lastData[joff + MotionEvent.SAMPLE_Y];
- distance = currDist;
- }
- }
-
- int badYDelta = Math.abs(yreplace - y);
- if (badYDelta > badPointerDistance) {
- badPointerDistance = badYDelta;
- badPointerIndex = i;
- badPointerReplaceXWith = xreplace;
- badPointerReplaceYWith = yreplace;
- }
- }
- }
- }
- if (badPointerIndex >= 0) {
- if (DEBUG_HACKS) {
- Slog.d("InputDevice", "Replacing bad pointer " + badPointerIndex +
- " with (" + badPointerReplaceXWith + ", " + badPointerReplaceYWith +
- ")");
- }
-
- final int offset = badPointerIndex * MotionEvent.NUM_SAMPLE_DATA;
- nextData[offset + MotionEvent.SAMPLE_X] = badPointerReplaceXWith;
- nextData[offset + MotionEvent.SAMPLE_Y] = badPointerReplaceYWith;
- mJumpyPointsDropped++;
- } else {
- mJumpyPointsDropped = 0;
- }
- }
-
- /**
- * Special hack for devices that have bad screen data: aggregate and
- * compute averages of the coordinate data, to reduce the amount of
- * jitter seen by applications.
- */
- int[] generateAveragedData(int upOrDownPointer, int lastNumPointers,
- int nextNumPointers) {
- final int numPointers = mLastNumPointers;
- final int[] rawData = mLastData;
- if (DEBUG_HACKS) Slog.v("InputDevice", "lastNumPointers=" + lastNumPointers
- + " nextNumPointers=" + nextNumPointers
- + " numPointers=" + numPointers);
- for (int i=0; i<numPointers; i++) {
- final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
- // We keep the average data in offsets based on the pointer
- // ID, so we don't need to move it around as fingers are
- // pressed and released.
- final int p = mPointerIds[i];
- final int poff = p * MotionEvent.NUM_SAMPLE_DATA * HISTORY_SIZE;
- if (i == upOrDownPointer && lastNumPointers != nextNumPointers) {
- if (lastNumPointers < nextNumPointers) {
- // This pointer is going down. Clear its history
- // and start fresh.
- if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer down @ index "
- + upOrDownPointer + " id " + mPointerIds[i]);
- mHistoryDataStart[i] = 0;
- mHistoryDataEnd[i] = 0;
- System.arraycopy(rawData, ioff, mHistoryData, poff,
- MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(rawData, ioff, mAveragedData, ioff,
- MotionEvent.NUM_SAMPLE_DATA);
- continue;
- } else {
- // The pointer is going up. Just fall through to
- // recompute the last averaged point (and don't add
- // it as a new point to include in the average).
- if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer up @ index "
- + upOrDownPointer + " id " + mPointerIds[i]);
- }
- } else {
- int end = mHistoryDataEnd[i];
- int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- int oldX = mHistoryData[eoff + MotionEvent.SAMPLE_X];
- int oldY = mHistoryData[eoff + MotionEvent.SAMPLE_Y];
- int newX = rawData[ioff + MotionEvent.SAMPLE_X];
- int newY = rawData[ioff + MotionEvent.SAMPLE_Y];
- int dx = newX-oldX;
- int dy = newY-oldY;
- int delta = dx*dx + dy*dy;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Delta from last: " + delta);
- if (delta >= (75*75)) {
- // Magic number, if moving farther than this, turn
- // off filtering to avoid lag in response.
- mHistoryDataStart[i] = 0;
- mHistoryDataEnd[i] = 0;
- System.arraycopy(rawData, ioff, mHistoryData, poff,
- MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(rawData, ioff, mAveragedData, ioff,
- MotionEvent.NUM_SAMPLE_DATA);
- continue;
- } else {
- end++;
- if (end >= HISTORY_SIZE) {
- end -= HISTORY_SIZE;
- }
- mHistoryDataEnd[i] = end;
- int noff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- mHistoryData[noff + MotionEvent.SAMPLE_X] = newX;
- mHistoryData[noff + MotionEvent.SAMPLE_Y] = newY;
- mHistoryData[noff + MotionEvent.SAMPLE_PRESSURE]
- = rawData[ioff + MotionEvent.SAMPLE_PRESSURE];
- int start = mHistoryDataStart[i];
- if (end == start) {
- start++;
- if (start >= HISTORY_SIZE) {
- start -= HISTORY_SIZE;
- }
- mHistoryDataStart[i] = start;
- }
- }
- }
-
- // Now compute the average.
- int start = mHistoryDataStart[i];
- int end = mHistoryDataEnd[i];
- int x=0, y=0;
- int totalPressure = 0;
- while (start != end) {
- int soff = poff + (start*MotionEvent.NUM_SAMPLE_DATA);
- int pressure = mHistoryData[soff + MotionEvent.SAMPLE_PRESSURE];
- if (pressure <= 0) pressure = 1;
- x += mHistoryData[soff + MotionEvent.SAMPLE_X] * pressure;
- y += mHistoryData[soff + MotionEvent.SAMPLE_Y] * pressure;
- totalPressure += pressure;
- start++;
- if (start >= HISTORY_SIZE) start = 0;
- }
- int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA);
- int pressure = mHistoryData[eoff + MotionEvent.SAMPLE_PRESSURE];
- if (pressure <= 0) pressure = 1;
- x += mHistoryData[eoff + MotionEvent.SAMPLE_X] * pressure;
- y += mHistoryData[eoff + MotionEvent.SAMPLE_Y] * pressure;
- totalPressure += pressure;
- x /= totalPressure;
- y /= totalPressure;
- if (DEBUG_HACKS) Slog.v("InputDevice", "Averaging " + totalPressure
- + " weight: (" + x + "," + y + ")");
- mAveragedData[ioff + MotionEvent.SAMPLE_X] = x;
- mAveragedData[ioff + MotionEvent.SAMPLE_Y] = y;
- mAveragedData[ioff + MotionEvent.SAMPLE_PRESSURE] =
- rawData[ioff + MotionEvent.SAMPLE_PRESSURE];
- mAveragedData[ioff + MotionEvent.SAMPLE_SIZE] =
- rawData[ioff + MotionEvent.SAMPLE_SIZE];
- }
- return mAveragedData;
- }
-
- private boolean assignPointer(int nextIndex, boolean allowOverlap) {
- final int lastNumPointers = mLastNumPointers;
- final int[] next2Last = mNext2Last;
- final long[] next2LastDistance = mNext2LastDistance;
- final int[] last2Next = mLast2Next;
- final int[] lastData = mLastData;
- final int[] nextData = mNextData;
- final int id = nextIndex * MotionEvent.NUM_SAMPLE_DATA;
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "assignPointer: nextIndex="
- + nextIndex + " dataOff=" + id);
- final int x1 = nextData[id + MotionEvent.SAMPLE_X];
- final int y1 = nextData[id + MotionEvent.SAMPLE_Y];
-
- long bestDistance = -1;
- int bestIndex = -1;
- for (int j=0; j<lastNumPointers; j++) {
- // If we are not allowing multiple new points to be assigned
- // to the same old pointer, then skip this one if it is already
- // detected as a conflict (-2).
- if (!allowOverlap && last2Next[j] < -1) {
- continue;
- }
- final int jd = j * MotionEvent.NUM_SAMPLE_DATA;
- final int xd = lastData[jd + MotionEvent.SAMPLE_X] - x1;
- final int yd = lastData[jd + MotionEvent.SAMPLE_Y] - y1;
- final long distance = xd*(long)xd + yd*(long)yd;
- if (bestDistance == -1 || distance < bestDistance) {
- bestDistance = distance;
- bestIndex = j;
- }
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "New index " + nextIndex
- + " best old index=" + bestIndex + " (distance="
- + bestDistance + ")");
- next2Last[nextIndex] = bestIndex;
- next2LastDistance[nextIndex] = bestDistance;
-
- if (bestIndex < 0) {
- return true;
- }
-
- if (last2Next[bestIndex] == -1) {
- last2Next[bestIndex] = nextIndex;
- return false;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Old index " + bestIndex
- + " has multiple best new pointers!");
-
- last2Next[bestIndex] = -2;
- return true;
- }
-
- private int updatePointerIdentifiers() {
- final int[] lastData = mLastData;
- final int[] nextData = mNextData;
- final int nextNumPointers = mNextNumPointers;
- final int lastNumPointers = mLastNumPointers;
-
- if (nextNumPointers == 1 && lastNumPointers == 1) {
- System.arraycopy(nextData, 0, lastData, 0,
- MotionEvent.NUM_SAMPLE_DATA);
- return -1;
- }
-
- // Clear our old state.
- final int[] last2Next = mLast2Next;
- for (int i=0; i<lastNumPointers; i++) {
- last2Next[i] = -1;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Update pointers: lastNumPointers=" + lastNumPointers
- + " nextNumPointers=" + nextNumPointers);
-
- // Figure out the closes new points to the previous points.
- final int[] next2Last = mNext2Last;
- final long[] next2LastDistance = mNext2LastDistance;
- boolean conflicts = false;
- for (int i=0; i<nextNumPointers; i++) {
- conflicts |= assignPointer(i, true);
- }
-
- // Resolve ambiguities in pointer mappings, when two or more
- // new pointer locations find their best previous location is
- // the same.
- if (conflicts) {
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving conflicts");
-
- for (int i=0; i<lastNumPointers; i++) {
- if (last2Next[i] != -2) {
- continue;
- }
-
- // Note that this algorithm is far from perfect. Ideally
- // we should do something like the one described at
- // http://portal.acm.org/citation.cfm?id=997856
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Resolving last index #" + i);
-
- int numFound;
- do {
- numFound = 0;
- long worstDistance = 0;
- int worstJ = -1;
- for (int j=0; j<nextNumPointers; j++) {
- if (next2Last[j] != i) {
- continue;
- }
- numFound++;
- if (worstDistance < next2LastDistance[j]) {
- worstDistance = next2LastDistance[j];
- worstJ = j;
- }
- }
-
- if (worstJ >= 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Worst new pointer: " + worstJ
- + " (distance=" + worstDistance + ")");
- if (assignPointer(worstJ, false)) {
- // In this case there is no last pointer
- // remaining for this new one!
- next2Last[worstJ] = -1;
- }
- }
- } while (numFound > 2);
- }
- }
-
- int retIndex = -1;
-
- if (lastNumPointers < nextNumPointers) {
- // We have one or more new pointers that are down. Create a
- // new pointer identifier for one of them.
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Adding new pointer");
- int nextId = 0;
- int i=0;
- while (i < lastNumPointers) {
- if (mPointerIds[i] > nextId) {
- // Found a hole, insert the pointer here.
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Inserting new pointer at hole " + i);
- System.arraycopy(mPointerIds, i, mPointerIds,
- i+1, lastNumPointers-i);
- System.arraycopy(lastData, i*MotionEvent.NUM_SAMPLE_DATA,
- lastData, (i+1)*MotionEvent.NUM_SAMPLE_DATA,
- (lastNumPointers-i)*MotionEvent.NUM_SAMPLE_DATA);
- System.arraycopy(next2Last, i, next2Last,
- i+1, lastNumPointers-i);
- break;
- }
- i++;
- nextId++;
- }
-
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "New pointer id " + nextId + " at index " + i);
-
- mLastNumPointers++;
- retIndex = i;
- mPointerIds[i] = nextId;
-
- // And assign this identifier to the first new pointer.
- for (int j=0; j<nextNumPointers; j++) {
- if (next2Last[j] < 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Assigning new id to new pointer index " + j);
- next2Last[j] = i;
- break;
- }
- }
- }
-
- // Propagate all of the current data into the appropriate
- // location in the old data to match the pointer ID that was
- // assigned to it.
- for (int i=0; i<nextNumPointers; i++) {
- int lastIndex = next2Last[i];
- if (lastIndex >= 0) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Copying next pointer index " + i
- + " to last index " + lastIndex);
- System.arraycopy(nextData, i*MotionEvent.NUM_SAMPLE_DATA,
- lastData, lastIndex*MotionEvent.NUM_SAMPLE_DATA,
- MotionEvent.NUM_SAMPLE_DATA);
- }
- }
-
- if (lastNumPointers > nextNumPointers) {
- // One or more pointers has gone up. Find the first one,
- // and adjust accordingly.
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Removing old pointer");
- for (int i=0; i<lastNumPointers; i++) {
- if (last2Next[i] == -1) {
- if (DEBUG_POINTERS) Slog.v("InputDevice",
- "Removing old pointer at index " + i);
- retIndex = i;
- break;
- }
- }
- }
-
- return retIndex;
- }
-
- void removeOldPointer(int index) {
- final int lastNumPointers = mLastNumPointers;
- if (index >= 0 && index < lastNumPointers) {
- System.arraycopy(mPointerIds, index+1, mPointerIds,
- index, lastNumPointers-index-1);
- System.arraycopy(mLastData, (index+1)*MotionEvent.NUM_SAMPLE_DATA,
- mLastData, (index)*MotionEvent.NUM_SAMPLE_DATA,
- (lastNumPointers-index-1)*MotionEvent.NUM_SAMPLE_DATA);
- mLastNumPointers--;
- }
- }
-
- MotionEvent generateAbsMotion(InputDevice device, long curTime,
- long curTimeNano, Display display, int orientation,
- int metaState) {
-
- if (mSkipLastPointers) {
- mSkipLastPointers = false;
- mLastNumPointers = 0;
- }
-
- if (mNextNumPointers <= 0 && mLastNumPointers <= 0) {
- return null;
- }
-
- final int lastNumPointers = mLastNumPointers;
- final int nextNumPointers = mNextNumPointers;
- if (mNextNumPointers > MAX_POINTERS) {
- Slog.w("InputDevice", "Number of pointers " + mNextNumPointers
- + " exceeded maximum of " + MAX_POINTERS);
- mNextNumPointers = MAX_POINTERS;
- }
-
- int upOrDownPointer = updatePointerIdentifiers();
-
- final float[] reportData = mReportData;
- final int[] rawData;
- if (KeyInputQueue.BAD_TOUCH_HACK) {
- rawData = generateAveragedData(upOrDownPointer, lastNumPointers,
- nextNumPointers);
- } else {
- rawData = mLastData;
- }
-
- final int numPointers = mLastNumPointers;
-
- if (DEBUG_POINTERS) Slog.v("InputDevice", "Processing "
- + numPointers + " pointers (going from " + lastNumPointers
- + " to " + nextNumPointers + ")");
-
- for (int i=0; i<numPointers; i++) {
- final int pos = i * MotionEvent.NUM_SAMPLE_DATA;
- reportData[pos + MotionEvent.SAMPLE_X] = rawData[pos + MotionEvent.SAMPLE_X];
- reportData[pos + MotionEvent.SAMPLE_Y] = rawData[pos + MotionEvent.SAMPLE_Y];
- reportData[pos + MotionEvent.SAMPLE_PRESSURE] = rawData[pos + MotionEvent.SAMPLE_PRESSURE];
- reportData[pos + MotionEvent.SAMPLE_SIZE] = rawData[pos + MotionEvent.SAMPLE_SIZE];
- }
-
- int action;
- int edgeFlags = 0;
- if (nextNumPointers != lastNumPointers) {
- if (nextNumPointers > lastNumPointers) {
- if (lastNumPointers == 0) {
- action = MotionEvent.ACTION_DOWN;
- mDownTime = curTime;
- } else {
- action = MotionEvent.ACTION_POINTER_DOWN
- | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- }
- } else {
- if (numPointers == 1) {
- action = MotionEvent.ACTION_UP;
- } else {
- action = MotionEvent.ACTION_POINTER_UP
- | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- }
- }
- currentMove = null;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
-
- final int dispW = display.getWidth()-1;
- final int dispH = display.getHeight()-1;
- int w = dispW;
- int h = dispH;
- if (orientation == Surface.ROTATION_90
- || orientation == Surface.ROTATION_270) {
- int tmp = w;
- w = h;
- h = tmp;
- }
-
- final AbsoluteInfo absX = device.absX;
- final AbsoluteInfo absY = device.absY;
- final AbsoluteInfo absPressure = device.absPressure;
- final AbsoluteInfo absSize = device.absSize;
- for (int i=0; i<numPointers; i++) {
- final int j = i * MotionEvent.NUM_SAMPLE_DATA;
-
- if (absX != null) {
- reportData[j + MotionEvent.SAMPLE_X] =
- ((reportData[j + MotionEvent.SAMPLE_X]-absX.minValue)
- / absX.range) * w;
- }
- if (absY != null) {
- reportData[j + MotionEvent.SAMPLE_Y] =
- ((reportData[j + MotionEvent.SAMPLE_Y]-absY.minValue)
- / absY.range) * h;
- }
- if (absPressure != null) {
- reportData[j + MotionEvent.SAMPLE_PRESSURE] =
- ((reportData[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue)
- / (float)absPressure.range);
- }
- if (absSize != null) {
- reportData[j + MotionEvent.SAMPLE_SIZE] =
- ((reportData[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue)
- / (float)absSize.range);
- }
-
- switch (orientation) {
- case Surface.ROTATION_90: {
- final float temp = reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_X] = reportData[j + MotionEvent.SAMPLE_Y];
- reportData[j + MotionEvent.SAMPLE_Y] = w-temp;
- break;
- }
- case Surface.ROTATION_180: {
- reportData[j + MotionEvent.SAMPLE_X] = w-reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_Y] = h-reportData[j + MotionEvent.SAMPLE_Y];
- break;
- }
- case Surface.ROTATION_270: {
- final float temp = reportData[j + MotionEvent.SAMPLE_X];
- reportData[j + MotionEvent.SAMPLE_X] = h-reportData[j + MotionEvent.SAMPLE_Y];
- reportData[j + MotionEvent.SAMPLE_Y] = temp;
- break;
- }
- }
- }
-
- // We only consider the first pointer when computing the edge
- // flags, since they are global to the event.
- if (action == MotionEvent.ACTION_DOWN) {
- if (reportData[MotionEvent.SAMPLE_X] <= 0) {
- edgeFlags |= MotionEvent.EDGE_LEFT;
- } else if (reportData[MotionEvent.SAMPLE_X] >= dispW) {
- edgeFlags |= MotionEvent.EDGE_RIGHT;
- }
- if (reportData[MotionEvent.SAMPLE_Y] <= 0) {
- edgeFlags |= MotionEvent.EDGE_TOP;
- } else if (reportData[MotionEvent.SAMPLE_Y] >= dispH) {
- edgeFlags |= MotionEvent.EDGE_BOTTOM;
- }
- }
-
- if (currentMove != null) {
- if (false) Slog.i("InputDevice", "Adding batch x="
- + reportData[MotionEvent.SAMPLE_X]
- + " y=" + reportData[MotionEvent.SAMPLE_Y]
- + " to " + currentMove);
- currentMove.addBatch(curTime, reportData, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
- }
-
- MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
- curTimeNano, action, numPointers, mPointerIds, reportData,
- metaState, xPrecision, yPrecision, device.id, edgeFlags);
- if (action == MotionEvent.ACTION_MOVE) {
- currentMove = me;
- }
-
- if (nextNumPointers < lastNumPointers) {
- removeOldPointer(upOrDownPointer);
- }
-
- return me;
- }
-
- boolean hasMore() {
- return mLastNumPointers != mNextNumPointers;
- }
-
- void finish() {
- mNextNumPointers = mAddingPointerOffset = 0;
- mNextData[MotionEvent.SAMPLE_PRESSURE] = 0;
- }
-
- MotionEvent generateRelMotion(InputDevice device, long curTime,
- long curTimeNano, int orientation, int metaState) {
-
- final float[] scaled = mReportData;
-
- // For now we only support 1 pointer with relative motions.
- scaled[MotionEvent.SAMPLE_X] = mNextData[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_Y] = mNextData[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f;
- scaled[MotionEvent.SAMPLE_SIZE] = 0;
- int edgeFlags = 0;
-
- int action;
- if (mNextNumPointers != mLastNumPointers) {
- mNextData[MotionEvent.SAMPLE_X] =
- mNextData[MotionEvent.SAMPLE_Y] = 0;
- if (mNextNumPointers > 0 && mLastNumPointers == 0) {
- action = MotionEvent.ACTION_DOWN;
- mDownTime = curTime;
- } else if (mNextNumPointers == 0) {
- action = MotionEvent.ACTION_UP;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
- mLastNumPointers = mNextNumPointers;
- currentMove = null;
- } else {
- action = MotionEvent.ACTION_MOVE;
- }
-
- scaled[MotionEvent.SAMPLE_X] *= xMoveScale;
- scaled[MotionEvent.SAMPLE_Y] *= yMoveScale;
- switch (orientation) {
- case Surface.ROTATION_90: {
- final float temp = scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_X] = scaled[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_Y] = -temp;
- break;
- }
- case Surface.ROTATION_180: {
- scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_Y] = -scaled[MotionEvent.SAMPLE_Y];
- break;
- }
- case Surface.ROTATION_270: {
- final float temp = scaled[MotionEvent.SAMPLE_X];
- scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_Y];
- scaled[MotionEvent.SAMPLE_Y] = temp;
- break;
- }
- }
-
- if (currentMove != null) {
- if (false) Slog.i("InputDevice", "Adding batch x="
- + scaled[MotionEvent.SAMPLE_X]
- + " y=" + scaled[MotionEvent.SAMPLE_Y]
- + " to " + currentMove);
- currentMove.addBatch(curTime, scaled, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
- }
-
- MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
- curTimeNano, action, 1, mPointerIds, scaled, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
- if (action == MotionEvent.ACTION_MOVE) {
- currentMove = me;
- }
- return me;
- }
- }
-
- static class AbsoluteInfo {
- int minValue;
- int maxValue;
- int range;
- int flat;
- int fuzz;
-
- final void dump(PrintWriter pw) {
- pw.print("minValue="); pw.print(minValue);
- pw.print(" maxValue="); pw.print(maxValue);
- pw.print(" range="); pw.print(range);
- pw.print(" flat="); pw.print(flat);
- pw.print(" fuzz="); pw.print(fuzz);
- }
- };
-
- InputDevice(int _id, int _classes, String _name,
- AbsoluteInfo _absX, AbsoluteInfo _absY,
- AbsoluteInfo _absPressure, AbsoluteInfo _absSize) {
- id = _id;
- classes = _classes;
- name = _name;
- absX = _absX;
- absY = _absY;
- absPressure = _absPressure;
- absSize = _absSize;
- }
-};
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
new file mode 100644
index 0000000..314dd8a
--- /dev/null
+++ b/services/java/com/android/server/InputManager.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.os.Environment;
+import android.os.LocalPowerManager;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.util.Xml;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.WindowManagerPolicy;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Properties;
+
+/*
+ * Wraps the C++ InputManager and provides its callbacks.
+ */
+public class InputManager {
+ static final String TAG = "InputManager";
+
+ private final Callbacks mCallbacks;
+ private final Context mContext;
+ private final WindowManagerService mWindowManagerService;
+
+ private int mTouchScreenConfig;
+ private int mKeyboardConfig;
+ private int mNavigationConfig;
+
+ private static native void nativeInit(Callbacks callbacks);
+ private static native void nativeStart();
+ private static native void nativeSetDisplaySize(int displayId, int width, int height);
+ private static native void nativeSetDisplayOrientation(int displayId, int rotation);
+
+ private static native int nativeGetScanCodeState(int deviceId, int sourceMask,
+ int scanCode);
+ private static native int nativeGetKeyCodeState(int deviceId, int sourceMask,
+ int keyCode);
+ private static native int nativeGetSwitchState(int deviceId, int sourceMask,
+ int sw);
+ private static native boolean nativeHasKeys(int deviceId, int sourceMask,
+ int[] keyCodes, boolean[] keyExists);
+ private static native void nativeRegisterInputChannel(InputChannel inputChannel,
+ boolean monitor);
+ private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+ private static native int nativeInjectInputEvent(InputEvent event,
+ int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
+ private static native void nativeSetInputWindows(InputWindow[] windows);
+ private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
+ private static native void nativeSetFocusedApplication(InputApplication application);
+ private static native void nativePreemptInputDispatch();
+ private static native InputDevice nativeGetInputDevice(int deviceId);
+ private static native int[] nativeGetInputDeviceIds();
+ private static native String nativeDump();
+
+ // Input event injection constants defined in InputDispatcher.h.
+ static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
+ static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
+ static final int INPUT_EVENT_INJECTION_FAILED = 2;
+ static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
+
+ // Input event injection synchronization modes defined in InputDispatcher.h
+ static final int INPUT_EVENT_INJECTION_SYNC_NONE = 0;
+ static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1;
+ static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH = 2;
+
+ // Key states (may be returned by queries about the current state of a
+ // particular key code, scan code or switch).
+
+ /** The key state is unknown or the requested key itself is not supported. */
+ public static final int KEY_STATE_UNKNOWN = -1;
+
+ /** The key is up. /*/
+ public static final int KEY_STATE_UP = 0;
+
+ /** The key is down. */
+ public static final int KEY_STATE_DOWN = 1;
+
+ /** The key is down but is a virtual key press that is being emulated by the system. */
+ public static final int KEY_STATE_VIRTUAL = 2;
+
+ public InputManager(Context context, WindowManagerService windowManagerService) {
+ this.mContext = context;
+ this.mWindowManagerService = windowManagerService;
+
+ this.mCallbacks = new Callbacks();
+
+ mTouchScreenConfig = Configuration.TOUCHSCREEN_NOTOUCH;
+ mKeyboardConfig = Configuration.KEYBOARD_NOKEYS;
+ mNavigationConfig = Configuration.NAVIGATION_NONAV;
+
+ init();
+ }
+
+ private void init() {
+ Slog.i(TAG, "Initializing input manager");
+ nativeInit(mCallbacks);
+ }
+
+ public void start() {
+ Slog.i(TAG, "Starting input manager");
+ nativeStart();
+ }
+
+ public void setDisplaySize(int displayId, int width, int height) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Invalid display id or dimensions.");
+ }
+
+ Slog.i(TAG, "Setting display #" + displayId + " size to " + width + "x" + height);
+ nativeSetDisplaySize(displayId, width, height);
+ }
+
+ public void setDisplayOrientation(int displayId, int rotation) {
+ if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
+ throw new IllegalArgumentException("Invalid rotation.");
+ }
+
+ Slog.i(TAG, "Setting display #" + displayId + " orientation to " + rotation);
+ nativeSetDisplayOrientation(displayId, rotation);
+ }
+
+ public void getInputConfiguration(Configuration config) {
+ if (config == null) {
+ throw new IllegalArgumentException("config must not be null.");
+ }
+
+ config.touchscreen = mTouchScreenConfig;
+ config.keyboard = mKeyboardConfig;
+ config.navigation = mNavigationConfig;
+ }
+
+ /**
+ * Gets the current state of a key or button by key code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param keyCode The key code to check.
+ * @return The key state.
+ */
+ public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) {
+ return nativeGetKeyCodeState(deviceId, sourceMask, keyCode);
+ }
+
+ /**
+ * Gets the current state of a key or button by scan code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param scanCode The scan code to check.
+ * @return The key state.
+ */
+ public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
+ return nativeGetScanCodeState(deviceId, sourceMask, scanCode);
+ }
+
+ /**
+ * Gets the current state of a switch by switch code.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param switchCode The switch code to check.
+ * @return The switch state.
+ */
+ public int getSwitchState(int deviceId, int sourceMask, int switchCode) {
+ return nativeGetSwitchState(deviceId, sourceMask, switchCode);
+ }
+
+ /**
+ * Determines whether the specified key codes are supported by a particular device.
+ * @param deviceId The input device id, or -1 to consult all devices.
+ * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
+ * consider all input sources. An input device is consulted if at least one of its
+ * non-class input source bits matches the specified source mask.
+ * @param keyCodes The array of key codes to check.
+ * @param keyExists An array at least as large as keyCodes whose entries will be set
+ * to true or false based on the presence or absence of support for the corresponding
+ * key codes.
+ * @return True if the lookup was successful, false otherwise.
+ */
+ public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
+ if (keyCodes == null) {
+ throw new IllegalArgumentException("keyCodes must not be null.");
+ }
+ if (keyExists == null || keyExists.length < keyCodes.length) {
+ throw new IllegalArgumentException("keyExists must not be null and must be at "
+ + "least as large as keyCodes.");
+ }
+
+ return nativeHasKeys(deviceId, sourceMask, keyCodes, keyExists);
+ }
+
+ /**
+ * Creates an input channel that will receive all input from the input dispatcher.
+ * @param inputChannelName The input channel name.
+ * @return The input channel.
+ */
+ public InputChannel monitorInput(String inputChannelName) {
+ if (inputChannelName == null) {
+ throw new IllegalArgumentException("inputChannelName must not be null.");
+ }
+
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
+ nativeRegisterInputChannel(inputChannels[0], true);
+ inputChannels[0].dispose(); // don't need to retain the Java object reference
+ return inputChannels[1];
+ }
+
+ /**
+ * Registers an input channel so that it can be used as an input event target.
+ * @param inputChannel The input channel to register.
+ */
+ public void registerInputChannel(InputChannel inputChannel) {
+ if (inputChannel == null) {
+ throw new IllegalArgumentException("inputChannel must not be null.");
+ }
+
+ nativeRegisterInputChannel(inputChannel, false);
+ }
+
+ /**
+ * Unregisters an input channel.
+ * @param inputChannel The input channel to unregister.
+ */
+ public void unregisterInputChannel(InputChannel inputChannel) {
+ if (inputChannel == null) {
+ throw new IllegalArgumentException("inputChannel must not be null.");
+ }
+
+ nativeUnregisterInputChannel(inputChannel);
+ }
+
+ /**
+ * Injects an input event into the event system on behalf of an application.
+ * The synchronization mode determines whether the method blocks while waiting for
+ * input injection to proceed.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_NONE} never blocks. Injection is asynchronous and
+ * is assumed always to be successful.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT} waits for previous events to be
+ * dispatched so that the input dispatcher can determine whether input event injection will
+ * be permitted based on the current input focus. Does not wait for the input event to
+ * finish processing.
+ *
+ * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH} waits for the input event to
+ * be completely processed.
+ *
+ * @param event The event to inject.
+ * @param injectorPid The pid of the injecting application.
+ * @param injectorUid The uid of the injecting application.
+ * @param syncMode The synchronization mode.
+ * @param timeoutMillis The injection timeout in milliseconds.
+ * @return One of the INPUT_EVENT_INJECTION_XXX constants.
+ */
+ public int injectInputEvent(InputEvent event, int injectorPid, int injectorUid,
+ int syncMode, int timeoutMillis) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+ if (injectorPid < 0 || injectorUid < 0) {
+ throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
+ }
+ if (timeoutMillis <= 0) {
+ throw new IllegalArgumentException("timeoutMillis must be positive");
+ }
+
+ return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
+ }
+
+ /**
+ * Gets information about the input device with the specified id.
+ * @param id The device id.
+ * @return The input device or null if not found.
+ */
+ public InputDevice getInputDevice(int deviceId) {
+ return nativeGetInputDevice(deviceId);
+ }
+
+ /**
+ * Gets the ids of all input devices in the system.
+ * @return The input device ids.
+ */
+ public int[] getInputDeviceIds() {
+ return nativeGetInputDeviceIds();
+ }
+
+ public void setInputWindows(InputWindow[] windows) {
+ nativeSetInputWindows(windows);
+ }
+
+ public void setFocusedApplication(InputApplication application) {
+ nativeSetFocusedApplication(application);
+ }
+
+ public void preemptInputDispatch() {
+ nativePreemptInputDispatch();
+ }
+
+ public void setInputDispatchMode(boolean enabled, boolean frozen) {
+ nativeSetInputDispatchMode(enabled, frozen);
+ }
+
+ public void dump(PrintWriter pw) {
+ String dumpStr = nativeDump();
+ if (dumpStr != null) {
+ pw.println(dumpStr);
+ }
+ }
+
+ private static final class VirtualKeyDefinition {
+ public int scanCode;
+
+ // configured position data, specified in display coords
+ public int centerX;
+ public int centerY;
+ public int width;
+ public int height;
+ }
+
+ private static final class InputDeviceCalibration {
+ public String[] keys;
+ public String[] values;
+ }
+
+ /*
+ * Callbacks from native.
+ */
+ private class Callbacks {
+ static final String TAG = "InputManager-Callbacks";
+
+ private static final boolean DEBUG_VIRTUAL_KEYS = false;
+ private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+ private static final String CALIBRATION_DIR_PATH = "usr/idc/";
+
+ @SuppressWarnings("unused")
+ public void virtualKeyDownFeedback() {
+ mWindowManagerService.mInputMonitor.virtualKeyDownFeedback();
+ }
+
+ @SuppressWarnings("unused")
+ public void notifyConfigurationChanged(long whenNanos,
+ int touchScreenConfig, int keyboardConfig, int navigationConfig) {
+ mTouchScreenConfig = touchScreenConfig;
+ mKeyboardConfig = keyboardConfig;
+ mNavigationConfig = navigationConfig;
+
+ mWindowManagerService.sendNewConfiguration();
+ }
+
+ @SuppressWarnings("unused")
+ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+ mWindowManagerService.mInputMonitor.notifyLidSwitchChanged(whenNanos, lidOpen);
+ }
+
+ @SuppressWarnings("unused")
+ public void notifyInputChannelBroken(InputChannel inputChannel) {
+ mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
+ }
+
+ @SuppressWarnings("unused")
+ public long notifyInputChannelANR(InputChannel inputChannel) {
+ return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel);
+ }
+
+ @SuppressWarnings("unused")
+ public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+ mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel);
+ }
+
+ @SuppressWarnings("unused")
+ public long notifyANR(Object token) {
+ return mWindowManagerService.mInputMonitor.notifyANR(token);
+ }
+
+ @SuppressWarnings("unused")
+ public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
+ int policyFlags, boolean isScreenOn) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
+ whenNanos, keyCode, down, policyFlags, isScreenOn);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean interceptKeyBeforeDispatching(InputChannel focus, int action,
+ int flags, int keyCode, int metaState, int repeatCount, int policyFlags) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
+ action, flags, keyCode, metaState, repeatCount, policyFlags);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
+ return mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @SuppressWarnings("unused")
+ public void notifyAppSwitchComing() {
+ mWindowManagerService.mInputMonitor.notifyAppSwitchComing();
+ }
+
+ @SuppressWarnings("unused")
+ public boolean filterTouchEvents() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_filterTouchEvents);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean filterJumpyTouchEvents() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_filterJumpyTouchEvents);
+ }
+
+ @SuppressWarnings("unused")
+ public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
+ ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
+
+ try {
+ FileInputStream fis = new FileInputStream(
+ "/sys/board_properties/virtualkeys." + deviceName);
+ InputStreamReader isr = new InputStreamReader(fis);
+ BufferedReader br = new BufferedReader(isr, 2048);
+ String str = br.readLine();
+ if (str != null) {
+ String[] it = str.split(":");
+ if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
+ final int N = it.length-6;
+ for (int i=0; i<=N; i+=6) {
+ if (!"0x01".equals(it[i])) {
+ Slog.w(TAG, "Unknown virtual key type at elem #"
+ + i + ": " + it[i] + " for device " + deviceName);
+ continue;
+ }
+ try {
+ VirtualKeyDefinition key = new VirtualKeyDefinition();
+ key.scanCode = Integer.parseInt(it[i+1]);
+ key.centerX = Integer.parseInt(it[i+2]);
+ key.centerY = Integer.parseInt(it[i+3]);
+ key.width = Integer.parseInt(it[i+4]);
+ key.height = Integer.parseInt(it[i+5]);
+ if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
+ + key.scanCode + ": center=" + key.centerX + ","
+ + key.centerY + " size=" + key.width + "x"
+ + key.height);
+ keys.add(key);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Bad number in virtual key definition at region "
+ + i + " in: " + str + " for device " + deviceName, e);
+ }
+ }
+ }
+ br.close();
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "No virtual keys found for device " + deviceName + ".");
+ } catch (IOException e) {
+ Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e);
+ }
+
+ return keys.toArray(new VirtualKeyDefinition[keys.size()]);
+ }
+
+ @SuppressWarnings("unused")
+ public InputDeviceCalibration getInputDeviceCalibration(String deviceName) {
+ // Calibration is specified as a sequence of colon-delimited key value pairs.
+ Properties properties = new Properties();
+ File calibrationFile = new File(Environment.getRootDirectory(),
+ CALIBRATION_DIR_PATH + deviceName + ".idc");
+ if (calibrationFile.exists()) {
+ try {
+ properties.load(new FileInputStream(calibrationFile));
+ } catch (IOException ex) {
+ Slog.w(TAG, "Error reading input device calibration properties for device "
+ + deviceName + " from " + calibrationFile + ".", ex);
+ }
+ } else {
+ Slog.i(TAG, "No input device calibration properties found for device "
+ + deviceName + ".");
+ return null;
+ }
+
+ InputDeviceCalibration calibration = new InputDeviceCalibration();
+ calibration.keys = properties.keySet().toArray(new String[properties.size()]);
+ calibration.values = properties.values().toArray(new String[properties.size()]);
+ return calibration;
+ }
+
+ @SuppressWarnings("unused")
+ public String[] getExcludedDeviceNames() {
+ ArrayList<String> names = new ArrayList<String>();
+
+ // Read partner-provided list of excluded input devices
+ XmlPullParser parser = null;
+ // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
+ File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
+ FileReader confreader = null;
+ try {
+ confreader = new FileReader(confFile);
+ parser = Xml.newPullParser();
+ parser.setInput(confreader);
+ XmlUtils.beginDocument(parser, "devices");
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (!"device".equals(parser.getName())) {
+ break;
+ }
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ names.add(name);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // It's ok if the file does not exist.
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
+ } finally {
+ try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+ }
+
+ return names.toArray(new String[names.size()]);
+ }
+
+ @SuppressWarnings("unused")
+ public int getMaxEventsPerSecond() {
+ int result = 0;
+ try {
+ result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec"));
+ } catch (NumberFormatException e) {
+ }
+ if (result < 1) {
+ result = 60;
+ }
+ return result;
+ }
+ }
+}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 5a995ae..c61baad 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -26,8 +26,7 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
-import com.android.server.status.IconData;
-import com.android.server.status.StatusBarService;
+import com.android.server.StatusBarManagerService;
import org.xmlpull.v1.XmlPullParserException;
@@ -113,9 +112,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final Context mContext;
final Handler mHandler;
final SettingsObserver mSettingsObserver;
- final StatusBarService mStatusBar;
- final IBinder mInputMethodIcon;
- final IconData mInputMethodData;
+ final StatusBarManagerService mStatusBar;
final IWindowManager mIWindowManager;
final HandlerCaller mCaller;
@@ -450,7 +447,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- public InputMethodManagerService(Context context, StatusBarService statusBar) {
+ public InputMethodManagerService(Context context, StatusBarManagerService statusBar) {
mContext = context;
mHandler = new Handler(this);
mIWindowManager = IWindowManager.Stub.asInterface(
@@ -470,9 +467,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
mStatusBar = statusBar;
- mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0);
- mInputMethodIcon = statusBar.addIcon(mInputMethodData, null);
- statusBar.setIconVisibility(mInputMethodIcon, false);
+ statusBar.setIconVisibility("ime", false);
buildInputMethodListLocked(mMethodList, mMethodMap);
@@ -480,8 +475,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mContext.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS);
Slog.i(TAG, "Enabled input methods: " + enabledStr);
- if (enabledStr == null) {
- Slog.i(TAG, "Enabled input methods has not been set, enabling all");
+ final String defaultIme = Settings.Secure.getString(mContext
+ .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ if (enabledStr == null || TextUtils.isEmpty(defaultIme)) {
+ Slog.i(TAG, "Enabled input methods or default IME has not been set, enabling all");
InputMethodInfo defIm = null;
StringBuilder sb = new StringBuilder(256);
final int N = mMethodList.size();
@@ -915,7 +912,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mEnabledSession = null;
mCurMethod = null;
}
- mStatusBar.setIconVisibility(mInputMethodIcon, false);
+ mStatusBar.setIconVisibility("ime", false);
}
public void onServiceDisconnected(ComponentName name) {
@@ -949,13 +946,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
if (iconId == 0) {
if (DEBUG) Slog.d(TAG, "hide the small icon for the input method");
- mStatusBar.setIconVisibility(mInputMethodIcon, false);
+ mStatusBar.setIconVisibility("ime", false);
} else if (packageName != null) {
if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
- mInputMethodData.iconId = iconId;
- mInputMethodData.iconPackage = packageName;
- mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null);
- mStatusBar.setIconVisibility(mInputMethodIcon, true);
+ mStatusBar.setIcon("ime", packageName, iconId, 0);
+ mStatusBar.setIconVisibility("ime", true);
}
}
} finally {
@@ -988,7 +983,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
void setInputMethodLocked(String id) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
- throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
+ throw new IllegalArgumentException("Unknown id: " + id);
}
if (id.equals(mCurMethodId)) {
@@ -1486,7 +1481,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
String defaultIme = Settings.Secure.getString(mContext
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
- if (!map.containsKey(defaultIme)) {
+ if (!TextUtils.isEmpty(defaultIme) && !map.containsKey(defaultIme)) {
if (chooseNewDefaultIMELocked()) {
updateFromSettingsLocked();
}
@@ -1738,8 +1733,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
p.println(" sessionRequested=" + ci.sessionRequested);
p.println(" curSession=" + ci.curSession);
}
- p.println(" mInputMethodIcon=" + mInputMethodIcon);
- p.println(" mInputMethodData=" + mInputMethodData);
p.println(" mCurMethodId=" + mCurMethodId);
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
new file mode 100644
index 0000000..8da0cf1
--- /dev/null
+++ b/services/java/com/android/server/InputWindow.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.view.InputChannel;
+
+/**
+ * Describes input-related window properties for use by the input dispatcher.
+ *
+ * @hide
+ */
+public final class InputWindow {
+ // The input channel associated with the window.
+ public InputChannel inputChannel;
+
+ // Window layout params attributes. (WindowManager.LayoutParams)
+ public int layoutParamsFlags;
+ public int layoutParamsType;
+
+ // Dispatching timeout.
+ public long dispatchingTimeoutNanos;
+
+ // Window frame position.
+ public int frameLeft;
+ public int frameTop;
+
+ // Window touchable area.
+ public int touchableAreaLeft;
+ public int touchableAreaTop;
+ public int touchableAreaRight;
+ public int touchableAreaBottom;
+
+ // Window is visible.
+ public boolean visible;
+
+ // Window has focus.
+ public boolean hasFocus;
+
+ // Window has wallpaper. (window is the current wallpaper target)
+ public boolean hasWallpaper;
+
+ // Input event dispatching is paused.
+ public boolean paused;
+
+ // Id of process and user that owns the window.
+ public int ownerPid;
+ public int ownerUid;
+
+ public void recycle() {
+ inputChannel = null;
+ }
+}
diff --git a/services/java/com/android/server/InputWindowList.java b/services/java/com/android/server/InputWindowList.java
new file mode 100644
index 0000000..1cbb2cc
--- /dev/null
+++ b/services/java/com/android/server/InputWindowList.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+
+/**
+ * A specialized list of window information objects backed by an array.
+ *
+ * This class is part of an InputManager optimization to avoid allocating objects and arrays
+ * unnecessarily. Internally, it keeps an array full of demand-allocated objects that it
+ * recycles each time the list is cleared. The used portion of the array is padded with a null.
+ *
+ * The contents of the list are intended to be Z-ordered from top to bottom.
+ *
+ * @hide
+ */
+public final class InputWindowList {
+ private InputWindow[] mArray;
+ private int mCount;
+
+ /**
+ * Creates an empty list.
+ */
+ public InputWindowList() {
+ mArray = new InputWindow[8];
+ }
+
+ /**
+ * Clears the list.
+ */
+ public void clear() {
+ if (mCount == 0) {
+ return;
+ }
+
+ int count = mCount;
+ mCount = 0;
+ mArray[count] = mArray[0];
+ while (count > 0) {
+ count -= 1;
+ mArray[count].recycle();
+ }
+ mArray[0] = null;
+ }
+
+ /**
+ * Adds an uninitialized input window object to the list and returns it.
+ */
+ public InputWindow add() {
+ if (mCount + 1 == mArray.length) {
+ InputWindow[] oldArray = mArray;
+ mArray = new InputWindow[oldArray.length * 2];
+ System.arraycopy(oldArray, 0, mArray, 0, mCount);
+ }
+
+ // Grab object from tail (after used section) if available.
+ InputWindow item = mArray[mCount + 1];
+ if (item == null) {
+ item = new InputWindow();
+ }
+
+ mArray[mCount] = item;
+ mCount += 1;
+ mArray[mCount] = null;
+ return item;
+ }
+
+ /**
+ * Gets the input window objects as a null-terminated array.
+ * @return The input window array.
+ */
+ public InputWindow[] toNullTerminatedArray() {
+ return mArray;
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java
index 2eaa58c..1f34eba 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/Installer.java
@@ -166,11 +166,17 @@ class Installer {
}
}
- public int install(String name, int uid, int gid) {
+ public int install(String name, boolean useEncryptedFilesystem, int uid, int gid) {
StringBuilder builder = new StringBuilder("install");
builder.append(' ');
builder.append(name);
builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
+ builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(gid);
@@ -203,33 +209,57 @@ class Installer {
return execute(builder.toString());
}
- public int remove(String name) {
+ public int remove(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("remove");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int rename(String oldname, String newname) {
+ public int rename(String oldname, String newname, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rename");
builder.append(' ');
builder.append(oldname);
builder.append(' ');
builder.append(newname);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int deleteCacheFiles(String name) {
+ public int deleteCacheFiles(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rmcache");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int clearUserData(String name) {
+ public int clearUserData(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rmuserdata");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
@@ -263,7 +293,7 @@ class Installer {
}
public int getSizeInfo(String pkgName, String apkPath,
- String fwdLockApkPath, PackageStats pStats) {
+ String fwdLockApkPath, PackageStats pStats, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
@@ -271,6 +301,13 @@ class Installer {
builder.append(apkPath);
builder.append(' ');
builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
+
String s = transaction(builder.toString());
String res[] = s.split(" ");
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
deleted file mode 100644
index 6d42141..0000000
--- a/services/java/com/android/server/KeyInputQueue.java
+++ /dev/null
@@ -1,1386 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Environment;
-import android.os.LatencyTimer;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.Xml;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.RawInputEvent;
-import android.view.Surface;
-import android.view.WindowManagerPolicy;
-
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-public abstract class KeyInputQueue {
- static final String TAG = "KeyInputQueue";
-
- static final boolean DEBUG = false;
- static final boolean DEBUG_VIRTUAL_KEYS = false;
- static final boolean DEBUG_POINTERS = false;
-
- /**
- * Turn on some hacks we have to improve the touch interaction with a
- * certain device whose screen currently is not all that good.
- */
- static boolean BAD_TOUCH_HACK = false;
-
- /**
- * Turn on some hacks to improve touch interaction with another device
- * where touch coordinate data can get corrupted.
- */
- static boolean JUMPY_TOUCH_HACK = false;
-
- private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
-
- final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
- final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
- final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
- final HapticFeedbackCallback mHapticFeedbackCallback;
-
- int mGlobalMetaState = 0;
- boolean mHaveGlobalMetaState = false;
-
- final QueuedEvent mFirst;
- final QueuedEvent mLast;
- QueuedEvent mCache;
- int mCacheCount;
-
- Display mDisplay = null;
- int mDisplayWidth;
- int mDisplayHeight;
-
- int mOrientation = Surface.ROTATION_0;
- int[] mKeyRotationMap = null;
-
- VirtualKey mPressedVirtualKey = null;
-
- PowerManager.WakeLock mWakeLock;
-
- static final int[] KEY_90_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
- };
-
- static final int[] KEY_180_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
- };
-
- static final int[] KEY_270_MAP = new int[] {
- KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
- KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
- };
-
- public static final int FILTER_REMOVE = 0;
- public static final int FILTER_KEEP = 1;
- public static final int FILTER_ABORT = -1;
-
- private static final boolean MEASURE_LATENCY = false;
- private LatencyTimer lt;
-
- public interface FilterCallback {
- int filterEvent(QueuedEvent ev);
- }
-
- public interface HapticFeedbackCallback {
- void virtualKeyFeedback(KeyEvent event);
- }
-
- static class QueuedEvent {
- InputDevice inputDevice;
- long whenNano;
- int flags; // From the raw event
- int classType; // One of the class constants in InputEvent
- Object event;
- boolean inQueue;
-
- void copyFrom(QueuedEvent that) {
- this.inputDevice = that.inputDevice;
- this.whenNano = that.whenNano;
- this.flags = that.flags;
- this.classType = that.classType;
- this.event = that.event;
- }
-
- @Override
- public String toString() {
- return "QueuedEvent{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + event + "}";
- }
-
- // not copied
- QueuedEvent prev;
- QueuedEvent next;
- }
-
- /**
- * A key that exists as a part of the touch-screen, outside of the normal
- * display area of the screen.
- */
- static class VirtualKey {
- int scancode;
- int centerx;
- int centery;
- int width;
- int height;
-
- int hitLeft;
- int hitTop;
- int hitRight;
- int hitBottom;
-
- InputDevice lastDevice;
- int lastKeycode;
-
- boolean checkHit(int x, int y) {
- return (x >= hitLeft && x <= hitRight
- && y >= hitTop && y <= hitBottom);
- }
-
- void computeHitRect(InputDevice dev, int dw, int dh) {
- if (dev == lastDevice) {
- return;
- }
-
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode
- + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
-
- lastDevice = dev;
-
- int minx = dev.absX.minValue;
- int maxx = dev.absX.maxValue;
-
- int halfw = width/2;
- int left = centerx - halfw;
- int right = centerx + halfw;
- hitLeft = minx + ((left*maxx-minx)/dw);
- hitRight = minx + ((right*maxx-minx)/dw);
-
- int miny = dev.absY.minValue;
- int maxy = dev.absY.maxValue;
-
- int halfh = height/2;
- int top = centery - halfh;
- int bottom = centery + halfh;
- hitTop = miny + ((top*maxy-miny)/dh);
- hitBottom = miny + ((bottom*maxy-miny)/dh);
- }
- }
-
- private void readVirtualKeys(String deviceName) {
- try {
- FileInputStream fis = new FileInputStream(
- "/sys/board_properties/virtualkeys." + deviceName);
- InputStreamReader isr = new InputStreamReader(fis);
- BufferedReader br = new BufferedReader(isr, 2048);
- String str = br.readLine();
- if (str != null) {
- String[] it = str.split(":");
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
- final int N = it.length-6;
- for (int i=0; i<=N; i+=6) {
- if (!"0x01".equals(it[i])) {
- Slog.w(TAG, "Unknown virtual key type at elem #" + i
- + ": " + it[i]);
- continue;
- }
- try {
- VirtualKey sb = new VirtualKey();
- sb.scancode = Integer.parseInt(it[i+1]);
- sb.centerx = Integer.parseInt(it[i+2]);
- sb.centery = Integer.parseInt(it[i+3]);
- sb.width = Integer.parseInt(it[i+4]);
- sb.height = Integer.parseInt(it[i+5]);
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
- + sb.scancode + ": center=" + sb.centerx + ","
- + sb.centery + " size=" + sb.width + "x"
- + sb.height);
- mVirtualKeys.add(sb);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Bad number at region " + i + " in: "
- + str, e);
- }
- }
- }
- br.close();
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "No virtual keys found");
- } catch (IOException e) {
- Slog.w(TAG, "Error reading virtual keys", e);
- }
- }
-
- private void readExcludedDevices() {
- // Read partner-provided list of excluded input devices
- XmlPullParser parser = null;
- // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
- File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
- FileReader confreader = null;
- try {
- confreader = new FileReader(confFile);
- parser = Xml.newPullParser();
- parser.setInput(confreader);
- XmlUtils.beginDocument(parser, "devices");
-
- while (true) {
- XmlUtils.nextElement(parser);
- if (!"device".equals(parser.getName())) {
- break;
- }
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name);
- addExcludedDevice(name);
- }
- }
- } catch (FileNotFoundException e) {
- // It's ok if the file does not exist.
- } catch (Exception e) {
- Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
- } finally {
- try { if (confreader != null) confreader.close(); } catch (IOException e) { }
- }
- }
-
- KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
- if (MEASURE_LATENCY) {
- lt = new LatencyTimer(100, 1000);
- }
-
- Resources r = context.getResources();
- BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);
-
- JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);
-
- mHapticFeedbackCallback = hapticFeedbackCallback;
-
- readExcludedDevices();
-
- PowerManager pm = (PowerManager)context.getSystemService(
- Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "KeyInputQueue");
- mWakeLock.setReferenceCounted(false);
-
- mFirst = new QueuedEvent();
- mLast = new QueuedEvent();
- mFirst.next = mLast;
- mLast.prev = mFirst;
- }
-
- void start() {
- mThread.start();
- }
-
- public void setDisplay(Display display) {
- mDisplay = display;
-
- // We assume at this point that the display dimensions reflect the
- // natural, unrotated display. We will perform hit tests for soft
- // buttons based on that display.
- mDisplayWidth = display.getWidth();
- mDisplayHeight = display.getHeight();
- }
-
- public void getInputConfiguration(Configuration config) {
- synchronized (mFirst) {
- config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
- config.keyboard = Configuration.KEYBOARD_NOKEYS;
- config.navigation = Configuration.NAVIGATION_NONAV;
-
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice d = mDevices.valueAt(i);
- if (d != null) {
- if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- config.touchscreen
- = Configuration.TOUCHSCREEN_FINGER;
- //Slog.i("foo", "***** HAVE TOUCHSCREEN!");
- }
- if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
- config.keyboard
- = Configuration.KEYBOARD_QWERTY;
- //Slog.i("foo", "***** HAVE QWERTY!");
- }
- if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- config.navigation
- = Configuration.NAVIGATION_TRACKBALL;
- //Slog.i("foo", "***** HAVE TRACKBALL!");
- } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
- config.navigation
- = Configuration.NAVIGATION_DPAD;
- //Slog.i("foo", "***** HAVE DPAD!");
- }
- }
- }
- }
- }
-
- public int getScancodeState(int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.scancode == code) {
- return 2;
- }
- }
- return nativeGetScancodeState(code);
- }
- }
-
- public int getScancodeState(int deviceId, int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.scancode == code) {
- return 2;
- }
- }
- return nativeGetScancodeState(deviceId, code);
- }
- }
-
- public int getTrackballScancodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- int res = nativeGetScancodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getDPadScancodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
- int res = nativeGetScancodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getKeycodeState(int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.lastKeycode == code) {
- return 2;
- }
- }
- return nativeGetKeycodeState(code);
- }
- }
-
- public int getKeycodeState(int deviceId, int code) {
- synchronized (mFirst) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk != null) {
- if (vk.lastKeycode == code) {
- return 2;
- }
- }
- return nativeGetKeycodeState(deviceId, code);
- }
- }
-
- public int getTrackballKeycodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- int res = nativeGetKeycodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public int getDPadKeycodeState(int code) {
- synchronized (mFirst) {
- final int N = mDevices.size();
- for (int i=0; i<N; i++) {
- InputDevice dev = mDevices.valueAt(i);
- if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
- int res = nativeGetKeycodeState(dev.id, code);
- if (res > 0) {
- return res;
- }
- }
- }
- }
-
- return 0;
- }
-
- public static native String getDeviceName(int deviceId);
- public static native int getDeviceClasses(int deviceId);
- public static native void addExcludedDevice(String deviceName);
- public static native boolean getAbsoluteInfo(int deviceId, int axis,
- InputDevice.AbsoluteInfo outInfo);
- public static native int getSwitchState(int sw);
- public static native int getSwitchState(int deviceId, int sw);
- public static native int nativeGetScancodeState(int code);
- public static native int nativeGetScancodeState(int deviceId, int code);
- public static native int nativeGetKeycodeState(int code);
- public static native int nativeGetKeycodeState(int deviceId, int code);
- public static native int scancodeToKeycode(int deviceId, int scancode);
- public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
-
- public static KeyEvent newKeyEvent(InputDevice device, long downTime,
- long eventTime, boolean down, int keycode, int repeatCount,
- int scancode, int flags) {
- return new KeyEvent(
- downTime, eventTime,
- down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
- keycode, repeatCount,
- device != null ? device.mMetaKeysState : 0,
- device != null ? device.id : -1, scancode,
- flags | KeyEvent.FLAG_FROM_SYSTEM);
- }
-
- Thread mThread = new Thread("InputDeviceReader") {
- public void run() {
- if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()");
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
- RawInputEvent ev = new RawInputEvent();
- while (true) {
- try {
- InputDevice di;
-
- // block, doesn't release the monitor
- readEvent(ev);
-
- boolean send = false;
- boolean configChanged = false;
-
- if (false) {
- Slog.i(TAG, "Input event: dev=0x"
- + Integer.toHexString(ev.deviceId)
- + " type=0x" + Integer.toHexString(ev.type)
- + " scancode=" + ev.scancode
- + " keycode=" + ev.keycode
- + " value=" + ev.value);
- }
-
- if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
- synchronized (mFirst) {
- di = newInputDevice(ev.deviceId);
- if (di.classes != 0) {
- // If this device is some kind of input class,
- // we care about it.
- mDevices.put(ev.deviceId, di);
- if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- readVirtualKeys(di.name);
- }
- // The configuration may have changed because
- // of this device.
- configChanged = true;
- } else {
- // We won't do anything with this device.
- mIgnoredDevices.put(ev.deviceId, di);
- Slog.i(TAG, "Ignoring non-input device: id=0x"
- + Integer.toHexString(di.id)
- + ", name=" + di.name);
- }
- }
- } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
- synchronized (mFirst) {
- if (false) {
- Slog.i(TAG, "Device removed: id=0x"
- + Integer.toHexString(ev.deviceId));
- }
- di = mDevices.get(ev.deviceId);
- if (di != null) {
- mDevices.delete(ev.deviceId);
- // The configuration may have changed because
- // of this device.
- configChanged = true;
- } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
- mIgnoredDevices.remove(ev.deviceId);
- } else {
- Slog.w(TAG, "Removing bad device id: "
- + Integer.toHexString(ev.deviceId));
- continue;
- }
- }
- } else {
- di = getInputDevice(ev.deviceId);
- if (di == null) {
- // This may be some junk from an ignored device.
- continue;
- }
-
- // first crack at it
- send = preprocessEvent(di, ev);
-
- if (ev.type == RawInputEvent.EV_KEY) {
- di.mMetaKeysState = makeMetaState(ev.keycode,
- ev.value != 0, di.mMetaKeysState);
- mHaveGlobalMetaState = false;
- }
- }
-
- if (configChanged) {
- synchronized (mFirst) {
- addLocked(di, System.nanoTime(), 0,
- RawInputEvent.CLASS_CONFIGURATION_CHANGED,
- null);
- }
- }
-
- if (!send) {
- continue;
- }
-
- synchronized (mFirst) {
- // NOTE: The event timebase absolutely must be the same
- // timebase as SystemClock.uptimeMillis().
- //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
- final long curTime = SystemClock.uptimeMillis();
- final long curTimeNano = System.nanoTime();
- //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
-
- final int classes = di.classes;
- final int type = ev.type;
- final int scancode = ev.scancode;
- send = false;
-
- // Is it a key event?
- if (type == RawInputEvent.EV_KEY &&
- (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
- (scancode < RawInputEvent.BTN_FIRST ||
- scancode > RawInputEvent.BTN_LAST)) {
- boolean down;
- if (ev.value != 0) {
- down = true;
- di.mKeyDownTime = curTime;
- } else {
- down = false;
- }
- int keycode = rotateKeyCodeLocked(ev.keycode);
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_KEYBOARD,
- newKeyEvent(di, di.mKeyDownTime, curTime, down,
- keycode, 0, scancode,
- ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
- ? KeyEvent.FLAG_WOKE_HERE : 0));
-
- } else if (ev.type == RawInputEvent.EV_KEY) {
- // Single touch protocol: touch going down or up.
- if (ev.scancode == RawInputEvent.BTN_TOUCH &&
- (classes&(RawInputEvent.CLASS_TOUCHSCREEN
- |RawInputEvent.CLASS_TOUCHSCREEN_MT))
- == RawInputEvent.CLASS_TOUCHSCREEN) {
- di.mAbs.changed = true;
- di.mAbs.mDown[0] = ev.value != 0;
-
- // Trackball (mouse) protocol: press down or up.
- } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
- (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- di.mRel.changed = true;
- di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
- send = true;
- }
-
- // Process position events from multitouch protocol.
- } else if (ev.type == RawInputEvent.EV_ABS &&
- (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
- if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_PRESSURE] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_X] = ev.value;
- if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
- + di.mAbs.mAddingPointerOffset
- + " X:" + ev.value);
- } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_Y] = ev.value;
- if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
- + di.mAbs.mAddingPointerOffset
- + " Y:" + ev.value);
- } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
- di.mAbs.changed = true;
- di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_SIZE] = ev.value;
- }
-
- // Process position events from single touch protocol.
- } else if (ev.type == RawInputEvent.EV_ABS &&
- (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- if (ev.scancode == RawInputEvent.ABS_X) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_Y) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
- di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
- + MotionEvent.SAMPLE_PRESSURE] = ev.value;
- } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
- di.mAbs.changed = true;
- di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
- di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
- + MotionEvent.SAMPLE_SIZE] = ev.value;
- }
-
- // Process movement events from trackball (mouse) protocol.
- } else if (ev.type == RawInputEvent.EV_REL &&
- (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
- // Add this relative movement into our totals.
- if (ev.scancode == RawInputEvent.REL_X) {
- di.mRel.changed = true;
- di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
- } else if (ev.scancode == RawInputEvent.REL_Y) {
- di.mRel.changed = true;
- di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
- }
- }
-
- // Handle multitouch protocol sync: tells us that the
- // driver has returned all data for -one- of the pointers
- // that is currently down.
- if (ev.type == RawInputEvent.EV_SYN
- && ev.scancode == RawInputEvent.SYN_MT_REPORT
- && di.mAbs != null) {
- di.mAbs.changed = true;
- if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
- // If the value is <= 0, the pointer is not
- // down, so keep it in the count.
-
- if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
- + MotionEvent.SAMPLE_PRESSURE] != 0) {
- final int num = di.mAbs.mNextNumPointers+1;
- di.mAbs.mNextNumPointers = num;
- if (DEBUG_POINTERS) Slog.v(TAG,
- "MT_REPORT: now have " + num + " pointers");
- final int newOffset = (num <= InputDevice.MAX_POINTERS)
- ? (num * MotionEvent.NUM_SAMPLE_DATA)
- : (InputDevice.MAX_POINTERS *
- MotionEvent.NUM_SAMPLE_DATA);
- di.mAbs.mAddingPointerOffset = newOffset;
- di.mAbs.mNextData[newOffset
- + MotionEvent.SAMPLE_PRESSURE] = 0;
- } else {
- if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer");
- }
- }
-
- // Handle general event sync: all data for the current
- // event update has been delivered.
- } else if (send || (ev.type == RawInputEvent.EV_SYN
- && ev.scancode == RawInputEvent.SYN_REPORT)) {
- if (mDisplay != null) {
- if (!mHaveGlobalMetaState) {
- computeGlobalMetaStateLocked();
- }
-
- MotionEvent me;
-
- InputDevice.MotionState ms = di.mAbs;
- if (ms.changed) {
- ms.everChanged = true;
- ms.changed = false;
-
- if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
- |RawInputEvent.CLASS_TOUCHSCREEN_MT))
- == RawInputEvent.CLASS_TOUCHSCREEN) {
- ms.mNextNumPointers = 0;
- if (ms.mDown[0]) {
- System.arraycopy(di.curTouchVals, 0,
- ms.mNextData, 0,
- MotionEvent.NUM_SAMPLE_DATA);
- ms.mNextNumPointers++;
- }
- }
-
- if (BAD_TOUCH_HACK) {
- ms.dropBadPoint(di);
- }
- if (JUMPY_TOUCH_HACK) {
- ms.dropJumpyPoint(di);
- }
-
- boolean doMotion = !monitorVirtualKey(di,
- ev, curTime, curTimeNano);
-
- if (doMotion && ms.mNextNumPointers > 0
- && (ms.mLastNumPointers == 0
- || ms.mSkipLastPointers)) {
- doMotion = !generateVirtualKeyDown(di,
- ev, curTime, curTimeNano);
- }
-
- if (doMotion) {
- // XXX Need to be able to generate
- // multiple events here, for example
- // if two fingers change up/down state
- // at the same time.
- do {
- me = ms.generateAbsMotion(di, curTime,
- curTimeNano, mDisplay,
- mOrientation, mGlobalMetaState);
- if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x="
- + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
- + " y="
- + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
- + " ev=" + me);
- if (me != null) {
- if (WindowManagerPolicy.WATCH_POINTER) {
- Slog.i(TAG, "Enqueueing: " + me);
- }
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_TOUCHSCREEN, me);
- }
- } while (ms.hasMore());
- } else {
- // We are consuming movement in the
- // virtual key area... but still
- // propagate this to the previous
- // data for comparisons.
- int num = ms.mNextNumPointers;
- if (num > InputDevice.MAX_POINTERS) {
- num = InputDevice.MAX_POINTERS;
- }
- System.arraycopy(ms.mNextData, 0,
- ms.mLastData, 0,
- num * MotionEvent.NUM_SAMPLE_DATA);
- ms.mLastNumPointers = num;
- ms.mSkipLastPointers = true;
- }
-
- ms.finish();
- }
-
- ms = di.mRel;
- if (ms.changed) {
- ms.everChanged = true;
- ms.changed = false;
-
- me = ms.generateRelMotion(di, curTime,
- curTimeNano,
- mOrientation, mGlobalMetaState);
- if (false) Slog.v(TAG, "Relative: x="
- + di.mRel.mNextData[MotionEvent.SAMPLE_X]
- + " y="
- + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
- + " ev=" + me);
- if (me != null) {
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_TRACKBALL, me);
- }
- }
- }
- }
- }
-
- } catch (RuntimeException exc) {
- Slog.e(TAG, "InputReaderThread uncaught exception", exc);
- }
- }
- }
- };
-
- private boolean isInsideDisplay(InputDevice dev) {
- final InputDevice.AbsoluteInfo absx = dev.absX;
- final InputDevice.AbsoluteInfo absy = dev.absY;
- final InputDevice.MotionState absm = dev.mAbs;
- if (absx == null || absy == null || absm == null) {
- return true;
- }
-
- if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
- && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
- && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
- && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input ("
- + absm.mNextData[MotionEvent.SAMPLE_X]
- + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
- + ") inside of display");
- return true;
- }
-
- return false;
- }
-
- private VirtualKey findVirtualKey(InputDevice dev) {
- final int N = mVirtualKeys.size();
- if (N <= 0) {
- return null;
- }
-
- final InputDevice.MotionState absm = dev.mAbs;
- for (int i=0; i<N; i++) {
- VirtualKey sb = mVirtualKeys.get(i);
- sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test ("
- + absm.mNextData[MotionEvent.SAMPLE_X] + ","
- + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
- + sb.scancode + " - (" + sb.hitLeft
- + "," + sb.hitTop + ")-(" + sb.hitRight + ","
- + sb.hitBottom + ")");
- if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
- absm.mNextData[MotionEvent.SAMPLE_Y])) {
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!");
- return sb;
- }
- }
-
- return null;
- }
-
- private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
- long curTime, long curTimeNano) {
- if (isInsideDisplay(di)) {
- // Didn't consume event.
- return false;
- }
-
-
- VirtualKey vk = findVirtualKey(di);
- if (vk != null) {
- final InputDevice.MotionState ms = di.mAbs;
- mPressedVirtualKey = vk;
- vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
- ms.mLastNumPointers = ms.mNextNumPointers;
- di.mKeyDownTime = curTime;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG,
- "Generate key down for: " + vk.scancode
- + " (keycode=" + vk.lastKeycode + ")");
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- }
-
- // We always consume the event, even if we didn't
- // generate a key event. There are two reasons for
- // this: to avoid spurious touches when holding
- // the edges of the device near the touchscreen,
- // and to avoid reporting events if there are virtual
- // keys on the touchscreen outside of the display
- // area.
- // Note that for all of this we are only looking at the
- // first pointer, since what we are handling here is the
- // first pointer going down, and this is the coordinate
- // that will be used to dispatch the event.
- if (false) {
- final InputDevice.AbsoluteInfo absx = di.absX;
- final InputDevice.AbsoluteInfo absy = di.absY;
- final InputDevice.MotionState absm = di.mAbs;
- Slog.v(TAG, "Rejecting ("
- + absm.mNextData[MotionEvent.SAMPLE_X] + ","
- + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
- + absx.minValue + "," + absy.minValue
- + ")-(" + absx.maxValue + ","
- + absx.maxValue + ")");
- }
- return true;
- }
-
- private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
- long curTime, long curTimeNano) {
- VirtualKey vk = mPressedVirtualKey;
- if (vk == null) {
- return false;
- }
-
- final InputDevice.MotionState ms = di.mAbs;
- if (ms.mNextNumPointers <= 0) {
- mPressedVirtualKey = null;
- ms.mLastNumPointers = 0;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode);
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- return true;
-
- } else if (isInsideDisplay(di)) {
- // Whoops the pointer has moved into
- // the display area! Cancel the
- // virtual key and start a pointer
- // motion.
- mPressedVirtualKey = null;
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode);
- KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
- vk.lastKeycode, 0, vk.scancode,
- KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
- mHapticFeedbackCallback.virtualKeyFeedback(event);
- addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
- event);
- ms.mLastNumPointers = 0;
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns a new meta state for the given keys and old state.
- */
- private static final int makeMetaState(int keycode, boolean down, int old) {
- int mask;
- switch (keycode) {
- case KeyEvent.KEYCODE_ALT_LEFT:
- mask = KeyEvent.META_ALT_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_ALT_RIGHT:
- mask = KeyEvent.META_ALT_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- mask = KeyEvent.META_SHIFT_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- mask = KeyEvent.META_SHIFT_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_SYM:
- mask = KeyEvent.META_SYM_ON;
- break;
- default:
- return old;
- }
- int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
- & (down ? (old | mask) : (old & ~mask));
- if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
- result |= KeyEvent.META_ALT_ON;
- }
- if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
- result |= KeyEvent.META_SHIFT_ON;
- }
- return result;
- }
-
- private void computeGlobalMetaStateLocked() {
- int i = mDevices.size();
- mGlobalMetaState = 0;
- while ((--i) >= 0) {
- mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
- }
- mHaveGlobalMetaState = true;
- }
-
- /*
- * Return true if you want the event to get passed on to the
- * rest of the system, and false if you've handled it and want
- * it dropped.
- */
- abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
-
- InputDevice getInputDevice(int deviceId) {
- synchronized (mFirst) {
- return getInputDeviceLocked(deviceId);
- }
- }
-
- private InputDevice getInputDeviceLocked(int deviceId) {
- return mDevices.get(deviceId);
- }
-
- public void setOrientation(int orientation) {
- synchronized(mFirst) {
- mOrientation = orientation;
- switch (orientation) {
- case Surface.ROTATION_90:
- mKeyRotationMap = KEY_90_MAP;
- break;
- case Surface.ROTATION_180:
- mKeyRotationMap = KEY_180_MAP;
- break;
- case Surface.ROTATION_270:
- mKeyRotationMap = KEY_270_MAP;
- break;
- default:
- mKeyRotationMap = null;
- break;
- }
- }
- }
-
- public int rotateKeyCode(int keyCode) {
- synchronized(mFirst) {
- return rotateKeyCodeLocked(keyCode);
- }
- }
-
- private int rotateKeyCodeLocked(int keyCode) {
- int[] map = mKeyRotationMap;
- if (map != null) {
- final int N = map.length;
- for (int i=0; i<N; i+=2) {
- if (map[i] == keyCode) {
- return map[i+1];
- }
- }
- }
- return keyCode;
- }
-
- boolean hasEvents() {
- synchronized (mFirst) {
- return mFirst.next != mLast;
- }
- }
-
- /*
- * returns true if we returned an event, and false if we timed out
- */
- QueuedEvent getEvent(long timeoutMS) {
- long begin = SystemClock.uptimeMillis();
- final long end = begin+timeoutMS;
- long now = begin;
- synchronized (mFirst) {
- while (mFirst.next == mLast && end > now) {
- try {
- mWakeLock.release();
- mFirst.wait(end-now);
- }
- catch (InterruptedException e) {
- }
- now = SystemClock.uptimeMillis();
- if (begin > now) {
- begin = now;
- }
- }
- if (mFirst.next == mLast) {
- return null;
- }
- QueuedEvent p = mFirst.next;
- mFirst.next = p.next;
- mFirst.next.prev = mFirst;
- p.inQueue = false;
- return p;
- }
- }
-
- /**
- * Return true if the queue has an up event pending that corresponds
- * to the same key as the given key event.
- */
- boolean hasKeyUpEvent(KeyEvent origEvent) {
- synchronized (mFirst) {
- final int keyCode = origEvent.getKeyCode();
- QueuedEvent cur = mLast.prev;
- while (cur.prev != null) {
- if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
- KeyEvent ke = (KeyEvent)cur.event;
- if (ke.getAction() == KeyEvent.ACTION_UP
- && ke.getKeyCode() == keyCode) {
- return true;
- }
- }
- cur = cur.prev;
- }
- }
-
- return false;
- }
-
- void recycleEvent(QueuedEvent ev) {
- synchronized (mFirst) {
- //Slog.i(TAG, "Recycle event: " + ev);
- if (ev.event == ev.inputDevice.mAbs.currentMove) {
- ev.inputDevice.mAbs.currentMove = null;
- }
- if (ev.event == ev.inputDevice.mRel.currentMove) {
- if (false) Slog.i(TAG, "Detach rel " + ev.event);
- ev.inputDevice.mRel.currentMove = null;
- ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
- ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
- }
- recycleLocked(ev);
- }
- }
-
- void filterQueue(FilterCallback cb) {
- synchronized (mFirst) {
- QueuedEvent cur = mLast.prev;
- while (cur.prev != null) {
- switch (cb.filterEvent(cur)) {
- case FILTER_REMOVE:
- cur.prev.next = cur.next;
- cur.next.prev = cur.prev;
- break;
- case FILTER_ABORT:
- return;
- }
- cur = cur.prev;
- }
- }
- }
-
- private QueuedEvent obtainLocked(InputDevice device, long whenNano,
- int flags, int classType, Object event) {
- QueuedEvent ev;
- if (mCacheCount == 0) {
- ev = new QueuedEvent();
- } else {
- ev = mCache;
- ev.inQueue = false;
- mCache = ev.next;
- mCacheCount--;
- }
- ev.inputDevice = device;
- ev.whenNano = whenNano;
- ev.flags = flags;
- ev.classType = classType;
- ev.event = event;
- return ev;
- }
-
- private void recycleLocked(QueuedEvent ev) {
- if (ev.inQueue) {
- throw new RuntimeException("Event already in queue!");
- }
- if (mCacheCount < 10) {
- mCacheCount++;
- ev.next = mCache;
- mCache = ev;
- ev.inQueue = true;
- }
- }
-
- private void addLocked(InputDevice device, long whenNano, int flags,
- int classType, Object event) {
- boolean poke = mFirst.next == mLast;
-
- QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
- QueuedEvent p = mLast.prev;
- while (p != mFirst && ev.whenNano < p.whenNano) {
- p = p.prev;
- }
-
- ev.next = p.next;
- ev.prev = p;
- p.next = ev;
- ev.next.prev = ev;
- ev.inQueue = true;
-
- if (poke) {
- long time;
- if (MEASURE_LATENCY) {
- time = System.nanoTime();
- }
- mFirst.notify();
- mWakeLock.acquire();
- if (MEASURE_LATENCY) {
- lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
- }
- }
- }
-
- private InputDevice newInputDevice(int deviceId) {
- int classes = getDeviceClasses(deviceId);
- String name = getDeviceName(deviceId);
- InputDevice.AbsoluteInfo absX = null;
- InputDevice.AbsoluteInfo absY = null;
- InputDevice.AbsoluteInfo absPressure = null;
- InputDevice.AbsoluteInfo absSize = null;
- if (classes != 0) {
- Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
- + ", name=" + name
- + ", classes=" + Integer.toHexString(classes));
- if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
- absX = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_POSITION_X, "X");
- absY = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_POSITION_Y, "Y");
- absPressure = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
- absSize = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
- } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- absX = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_X, "X");
- absY = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_Y, "Y");
- absPressure = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_PRESSURE, "Pressure");
- absSize = loadAbsoluteInfo(deviceId,
- RawInputEvent.ABS_TOOL_WIDTH, "Size");
- }
- }
-
- return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
- }
-
- private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
- String name) {
- InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
- if (getAbsoluteInfo(id, channel, info)
- && info.minValue != info.maxValue) {
- Slog.i(TAG, " " + name + ": min=" + info.minValue
- + " max=" + info.maxValue
- + " flat=" + info.flat
- + " fuzz=" + info.fuzz);
- info.range = info.maxValue-info.minValue;
- return info;
- }
- Slog.i(TAG, " " + name + ": unknown values");
- return null;
- }
- private static native boolean readEvent(RawInputEvent outEvent);
-
- void dump(PrintWriter pw, String prefix) {
- synchronized (mFirst) {
- for (int i=0; i<mDevices.size(); i++) {
- InputDevice dev = mDevices.valueAt(i);
- pw.print(prefix); pw.print("Device #");
- pw.print(mDevices.keyAt(i)); pw.print(" ");
- pw.print(dev.name); pw.print(" (classes=0x");
- pw.print(Integer.toHexString(dev.classes));
- pw.println("):");
- pw.print(prefix); pw.print(" mKeyDownTime=");
- pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState=");
- pw.println(dev.mMetaKeysState);
- if (dev.absX != null) {
- pw.print(prefix); pw.print(" absX: "); dev.absX.dump(pw);
- pw.println("");
- }
- if (dev.absY != null) {
- pw.print(prefix); pw.print(" absY: "); dev.absY.dump(pw);
- pw.println("");
- }
- if (dev.absPressure != null) {
- pw.print(prefix); pw.print(" absPressure: ");
- dev.absPressure.dump(pw); pw.println("");
- }
- if (dev.absSize != null) {
- pw.print(prefix); pw.print(" absSize: ");
- dev.absSize.dump(pw); pw.println("");
- }
- if (dev.mAbs.everChanged) {
- pw.print(prefix); pw.println(" mAbs:");
- dev.mAbs.dump(pw, prefix + " ");
- }
- if (dev.mRel.everChanged) {
- pw.print(prefix); pw.println(" mRel:");
- dev.mRel.dump(pw, prefix + " ");
- }
- }
- pw.println(" ");
- for (int i=0; i<mIgnoredDevices.size(); i++) {
- InputDevice dev = mIgnoredDevices.valueAt(i);
- pw.print(prefix); pw.print("Ignored Device #");
- pw.print(mIgnoredDevices.keyAt(i)); pw.print(" ");
- pw.print(dev.name); pw.print(" (classes=0x");
- pw.print(Integer.toHexString(dev.classes));
- pw.println(")");
- }
- pw.println(" ");
- for (int i=0; i<mVirtualKeys.size(); i++) {
- VirtualKey vk = mVirtualKeys.get(i);
- pw.print(prefix); pw.print("Virtual Key #");
- pw.print(i); pw.println(":");
- pw.print(prefix); pw.print(" scancode="); pw.println(vk.scancode);
- pw.print(prefix); pw.print(" centerx="); pw.print(vk.centerx);
- pw.print(" centery="); pw.print(vk.centery);
- pw.print(" width="); pw.print(vk.width);
- pw.print(" height="); pw.println(vk.height);
- pw.print(prefix); pw.print(" hitLeft="); pw.print(vk.hitLeft);
- pw.print(" hitTop="); pw.print(vk.hitTop);
- pw.print(" hitRight="); pw.print(vk.hitRight);
- pw.print(" hitBottom="); pw.println(vk.hitBottom);
- if (vk.lastDevice != null) {
- pw.print(prefix); pw.print(" lastDevice=#");
- pw.println(vk.lastDevice.id);
- }
- if (vk.lastKeycode != 0) {
- pw.print(prefix); pw.print(" lastKeycode=");
- pw.println(vk.lastKeycode);
- }
- }
- pw.println(" ");
- pw.print(prefix); pw.print(" Default keyboard: ");
- pw.println(SystemProperties.get("hw.keyboards.0.devname"));
- pw.print(prefix); pw.print(" mGlobalMetaState=");
- pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState=");
- pw.println(mHaveGlobalMetaState);
- pw.print(prefix); pw.print(" mDisplayWidth=");
- pw.print(mDisplayWidth); pw.print(" mDisplayHeight=");
- pw.println(mDisplayHeight);
- pw.print(prefix); pw.print(" mOrientation=");
- pw.println(mOrientation);
- if (mPressedVirtualKey != null) {
- pw.print(prefix); pw.print(" mPressedVirtualKey.scancode=");
- pw.println(mPressedVirtualKey.scancode);
- }
- }
- }
-}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 65f4194..a38970f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,17 +16,6 @@
package com.android.server;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Observable;
-import java.util.Observer;
-import java.util.Set;
-
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -41,6 +30,7 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.location.Address;
+import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
@@ -50,7 +40,6 @@ import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -68,12 +57,27 @@ import android.util.Log;
import android.util.Slog;
import android.util.PrintWriterPrinter;
-import com.android.internal.location.GeocoderProxy;
-import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.GpsNetInitiatedHandler;
-import com.android.internal.location.LocationProviderProxy;
-import com.android.internal.location.MockProvider;
-import com.android.internal.location.PassiveProvider;
+
+import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationProviderInterface;
+import com.android.server.location.LocationProviderProxy;
+import com.android.server.location.MockProvider;
+import com.android.server.location.PassiveProvider;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Set;
/**
* The service class that manages LocationProviders and issues location
@@ -609,10 +613,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return out;
}
- public List<String> getProviders(boolean enabledOnly) {
+ public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
try {
synchronized (mLock) {
- return _getProvidersLocked(enabledOnly);
+ return _getProvidersLocked(criteria, enabledOnly);
}
} catch (SecurityException se) {
throw se;
@@ -622,7 +626,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private List<String> _getProvidersLocked(boolean enabledOnly) {
+ private List<String> _getProvidersLocked(Criteria criteria, boolean enabledOnly) {
if (LOCAL_LOGV) {
Slog.v(TAG, "getProviders");
}
@@ -634,25 +638,242 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (enabledOnly && !isAllowedBySettingsLocked(name)) {
continue;
}
+ if (criteria != null && !p.meetsCriteria(criteria)) {
+ continue;
+ }
out.add(name);
}
}
return out;
}
+ /**
+ * Returns the next looser power requirement, in the sequence:
+ *
+ * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
+ */
+ private int nextPower(int power) {
+ switch (power) {
+ case Criteria.POWER_LOW:
+ return Criteria.POWER_MEDIUM;
+ case Criteria.POWER_MEDIUM:
+ return Criteria.POWER_HIGH;
+ case Criteria.POWER_HIGH:
+ return Criteria.NO_REQUIREMENT;
+ case Criteria.NO_REQUIREMENT:
+ default:
+ return Criteria.NO_REQUIREMENT;
+ }
+ }
+
+ /**
+ * Returns the next looser accuracy requirement, in the sequence:
+ *
+ * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
+ */
+ private int nextAccuracy(int accuracy) {
+ if (accuracy == Criteria.ACCURACY_FINE) {
+ return Criteria.ACCURACY_COARSE;
+ } else {
+ return Criteria.NO_REQUIREMENT;
+ }
+ }
+
+ private class LpPowerComparator implements Comparator<LocationProviderInterface> {
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ // Smaller is better
+ return (l1.getPowerRequirement() - l2.getPowerRequirement());
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (l1.getPowerRequirement() == l2.getPowerRequirement());
+ }
+ }
+
+ private class LpAccuracyComparator implements Comparator<LocationProviderInterface> {
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ // Smaller is better
+ return (l1.getAccuracy() - l2.getAccuracy());
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (l1.getAccuracy() == l2.getAccuracy());
+ }
+ }
+
+ private class LpCapabilityComparator implements Comparator<LocationProviderInterface> {
+
+ private static final int ALTITUDE_SCORE = 4;
+ private static final int BEARING_SCORE = 4;
+ private static final int SPEED_SCORE = 4;
+
+ private int score(LocationProviderInterface p) {
+ return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
+ (p.supportsBearing() ? BEARING_SCORE : 0) +
+ (p.supportsSpeed() ? SPEED_SCORE : 0);
+ }
+
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (score(l2) - score(l1)); // Bigger is better
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (score(l1) == score(l2));
+ }
+ }
+
+ private LocationProviderInterface best(List<String> providerNames) {
+ ArrayList<LocationProviderInterface> providers;
+ synchronized (mLock) {
+ providers = new ArrayList<LocationProviderInterface>(mProviders.size());
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ providers.add(mProviders.get(i));
+ }
+ }
+
+ if (providers.size() < 2) {
+ return providers.get(0);
+ }
+
+ // First, sort by power requirement
+ Collections.sort(providers, new LpPowerComparator());
+ int power = providers.get(0).getPowerRequirement();
+ if (power < providers.get(1).getPowerRequirement()) {
+ return providers.get(0);
+ }
+
+ int idx, size;
+
+ ArrayList<LocationProviderInterface> tmp = new ArrayList<LocationProviderInterface>();
+ idx = 0;
+ size = providers.size();
+ while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
+ tmp.add(providers.get(idx));
+ idx++;
+ }
+
+ // Next, sort by accuracy
+ Collections.sort(tmp, new LpAccuracyComparator());
+ int acc = tmp.get(0).getAccuracy();
+ if (acc < tmp.get(1).getAccuracy()) {
+ return tmp.get(0);
+ }
+
+ ArrayList<LocationProviderInterface> tmp2 = new ArrayList<LocationProviderInterface>();
+ idx = 0;
+ size = tmp.size();
+ while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
+ tmp2.add(tmp.get(idx));
+ idx++;
+ }
+
+ // Finally, sort by capability "score"
+ Collections.sort(tmp2, new LpCapabilityComparator());
+ return tmp2.get(0);
+ }
+
+ /**
+ * Returns the name of the provider that best meets the given criteria. Only providers
+ * that are permitted to be accessed by the calling activity will be
+ * returned. If several providers meet the criteria, the one with the best
+ * accuracy is returned. If no provider meets the criteria,
+ * the criteria are loosened in the following sequence:
+ *
+ * <ul>
+ * <li> power requirement
+ * <li> accuracy
+ * <li> bearing
+ * <li> speed
+ * <li> altitude
+ * </ul>
+ *
+ * <p> Note that the requirement on monetary cost is not removed
+ * in this process.
+ *
+ * @param criteria the criteria that need to be matched
+ * @param enabledOnly if true then only a provider that is currently enabled is returned
+ * @return name of the provider that best matches the requirements
+ */
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+ List<String> goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Make a copy of the criteria that we can modify
+ criteria = new Criteria(criteria);
+
+ // Loosen power requirement
+ int power = criteria.getPowerRequirement();
+ while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
+ power = nextPower(power);
+ criteria.setPowerRequirement(power);
+ goodProviders = getProviders(criteria, enabledOnly);
+ }
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Loosen accuracy requirement
+ int accuracy = criteria.getAccuracy();
+ while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
+ accuracy = nextAccuracy(accuracy);
+ criteria.setAccuracy(accuracy);
+ goodProviders = getProviders(criteria, enabledOnly);
+ }
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove bearing requirement
+ criteria.setBearingRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove speed requirement
+ criteria.setSpeedRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove altitude requirement
+ criteria.setAltitudeRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ return null;
+ }
+
+ public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) {
+ throw new IllegalArgumentException("provider=" + provider);
+ }
+ return p.meetsCriteria(criteria);
+ }
+
private void updateProvidersLocked() {
+ boolean changesMade = false;
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface p = mProviders.get(i);
boolean isEnabled = p.isEnabled();
String name = p.getName();
boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
-
if (isEnabled && !shouldBeEnabled) {
updateProviderListenersLocked(name, false);
+ changesMade = true;
} else if (!isEnabled && shouldBeEnabled) {
updateProviderListenersLocked(name, true);
+ changesMade = true;
}
-
+ }
+ if (changesMade) {
+ mContext.sendBroadcast(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
}
}
@@ -716,6 +937,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
final Receiver mReceiver;
final long mMinTime;
final float mMinDistance;
+ final boolean mSingleShot;
final int mUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
@@ -723,12 +945,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, long minTime, float minDistance,
+ UpdateRecord(String provider, long minTime, float minDistance, boolean singleShot,
Receiver receiver, int uid) {
mProvider = provider;
mReceiver = receiver;
mMinTime = minTime;
mMinDistance = minDistance;
+ mSingleShot = singleShot;
mUid = uid;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -763,6 +986,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
pw.println(prefix + this);
pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
+ pw.println(prefix + "mSingleShot=" + mSingleShot);
pw.println(prefix + "mUid=" + mUid);
pw.println(prefix + "mLastFixBroadcast:");
if (mLastFixBroadcast != null) {
@@ -818,12 +1042,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return false;
}
- public void requestLocationUpdates(String provider,
- long minTime, float minDistance, ILocationListener listener) {
-
+ public void requestLocationUpdates(String provider, Criteria criteria,
+ long minTime, float minDistance, boolean singleShot, ILocationListener listener) {
+ if (criteria != null) {
+ // FIXME - should we consider using multiple providers simultaneously
+ // rather than only the best one?
+ // Should we do anything different for single shot fixes?
+ provider = getBestProvider(criteria, true);
+ if (provider == null) {
+ throw new IllegalArgumentException("no providers found for criteria");
+ }
+ }
try {
synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
+ requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
+ getReceiver(listener));
}
} catch (SecurityException se) {
throw se;
@@ -834,11 +1067,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- public void requestLocationUpdatesPI(String provider,
- long minTime, float minDistance, PendingIntent intent) {
+ public void requestLocationUpdatesPI(String provider, Criteria criteria,
+ long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
+ if (criteria != null) {
+ // FIXME - should we consider using multiple providers simultaneously
+ // rather than only the best one?
+ // Should we do anything different for single shot fixes?
+ provider = getBestProvider(criteria, true);
+ if (provider == null) {
+ throw new IllegalArgumentException("no providers found for criteria");
+ }
+ }
try {
synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
+ requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
+ getReceiver(intent));
}
} catch (SecurityException se) {
throw se;
@@ -849,8 +1092,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private void requestLocationUpdatesLocked(String provider,
- long minTime, float minDistance, Receiver receiver) {
+ private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance,
+ boolean singleShot, Receiver receiver) {
if (LOCAL_LOGV) {
Slog.v(TAG, "_requestLocationUpdates: listener = " + receiver);
}
@@ -867,7 +1110,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
boolean newUid = !providerHasListener(provider, callingUid, null);
long identity = Binder.clearCallingIdentity();
try {
- UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
+ UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, singleShot,
+ receiver, callingUid);
UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
if (oldRecord != null) {
oldRecord.disposeLocked();
@@ -881,7 +1125,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (isProviderEnabled) {
long minTimeForProvider = getMinTimeLocked(provider);
p.setMinTime(minTimeForProvider);
- p.enableLocationTracking(true);
+ // try requesting single shot if singleShot is true, and fall back to
+ // regular location tracking if requestSingleShotFix() is not supported
+ if (!singleShot || !p.requestSingleShotFix()) {
+ p.enableLocationTracking(true);
+ }
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(provider, false);
@@ -1287,7 +1535,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface provider = mProviders.get(i);
- requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
+ requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f,
+ false, mProximityReceiver);
}
}
}
@@ -1368,8 +1617,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
} catch (SecurityException se) {
throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
} catch (Exception e) {
Slog.e(TAG, "isProviderEnabled got exception:", e);
return false;
@@ -1393,7 +1640,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
+ return false;
}
return isAllowedBySettingsLocked(provider);
}
@@ -1405,8 +1652,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
} catch (SecurityException se) {
throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
} catch (Exception e) {
Slog.e(TAG, "getLastKnownLocation got exception:", e);
return null;
@@ -1418,7 +1663,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
+ return null;
}
if (!isAllowedBySettingsLocked(provider)) {
@@ -1485,6 +1730,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
for (int i=0; i<N; i++) {
UpdateRecord r = records.get(i);
Receiver receiver = r.mReceiver;
+ boolean receiverDead = false;
Location lastLoc = r.mLastFixBroadcast;
if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
@@ -1496,10 +1742,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
if (!receiver.callLocationChangedLocked(location)) {
Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- deadReceivers.add(receiver);
+ receiverDead = true;
}
}
@@ -1509,13 +1752,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
r.mLastStatusBroadcast = newStatusUpdateTime;
if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+ receiverDead = true;
Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- if (!deadReceivers.contains(receiver)) {
- deadReceivers.add(receiver);
- }
+ }
+ }
+
+ // remove receiver if it is dead or we just processed a single shot request
+ if (receiverDead || r.mSingleShot) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<Receiver>();
+ }
+ if (!deadReceivers.contains(receiver)) {
+ deadReceivers.add(receiver);
}
}
}
@@ -1692,6 +1940,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Geocoder
+ public boolean geocoderIsImplemented() {
+ return mGeocodeProvider != null;
+ }
+
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
if (mGeocodeProvider != null) {
diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java
index 27a8a74..4d04cee 100644
--- a/services/java/com/android/server/MasterClearReceiver.java
+++ b/services/java/com/android/server/MasterClearReceiver.java
@@ -39,7 +39,11 @@ public class MasterClearReceiver extends BroadcastReceiver {
try {
Slog.w(TAG, "!!! FACTORY RESET !!!");
- RecoverySystem.rebootWipeUserData(context);
+ if (intent.hasExtra("enableEFS")) {
+ RecoverySystem.rebootToggleEFS(context, intent.getBooleanExtra("enableEFS", false));
+ } else {
+ RecoverySystem.rebootWipeUserData(context);
+ }
Log.wtf(TAG, "Still running after master clear?!");
} catch (IOException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index cb55808..fded623 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,31 +16,42 @@
package com.android.server;
+import com.android.internal.app.IMediaContainerService;
import com.android.server.am.ActivityManagerService;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.content.res.ObbInfo;
import android.net.Uri;
-import android.os.storage.IMountService;
-import android.os.storage.IMountServiceListener;
-import android.os.storage.IMountShutdownObserver;
-import android.os.storage.StorageResultCode;
+import android.os.Binder;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Environment;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.os.storage.IMountShutdownObserver;
+import android.os.storage.IObbActionListener;
+import android.os.storage.StorageResultCode;
import android.util.Slog;
+
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
/**
* MountService implements back-end services for platform storage
@@ -53,7 +64,8 @@ class MountService extends IMountService.Stub
private static final boolean LOCAL_LOGD = false;
private static final boolean DEBUG_UNMOUNT = false;
private static final boolean DEBUG_EVENTS = false;
-
+ private static final boolean DEBUG_OBB = true;
+
private static final String TAG = "MountService";
/*
@@ -130,6 +142,80 @@ class MountService extends IMountService.Stub
*/
final private HashSet<String> mAsecMountSet = new HashSet<String>();
+ /**
+ * Mounted OBB tracking information. Used to track the current state of all
+ * OBBs.
+ */
+ final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>();
+ final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
+
+ class ObbState implements IBinder.DeathRecipient {
+ public ObbState(String filename, IObbActionListener token, int callerUid) {
+ this.filename = filename;
+ this.token = token;
+ this.callerUid = callerUid;
+ mounted = false;
+ }
+
+ // OBB source filename
+ String filename;
+
+ // Token of remote Binder caller
+ IObbActionListener token;
+
+ // Binder.callingUid()
+ public int callerUid;
+
+ // Whether this is mounted currently.
+ boolean mounted;
+
+ @Override
+ public void binderDied() {
+ ObbAction action = new UnmountObbAction(this, true);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ removeObbState(this);
+
+ token.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+
+ // OBB Action Handler
+ final private ObbActionHandler mObbActionHandler;
+
+ // OBB action handler messages
+ private static final int OBB_RUN_ACTION = 1;
+ private static final int OBB_MCS_BOUND = 2;
+ private static final int OBB_MCS_UNBIND = 3;
+ private static final int OBB_MCS_RECONNECT = 4;
+ private static final int OBB_MCS_GIVE_UP = 5;
+
+ /*
+ * Default Container Service information
+ */
+ static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
+
+ final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
+
+ class DefaultContainerConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "onServiceConnected");
+ IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "onServiceDisconnected");
+ }
+ };
+
+ // Used in the ObbActionHandler
+ private IMediaContainerService mContainerService = null;
+
+ // Handler messages
private static final int H_UNMOUNT_PM_UPDATE = 1;
private static final int H_UNMOUNT_PM_DONE = 2;
private static final int H_UNMOUNT_MS = 3;
@@ -287,7 +373,7 @@ class MountService extends IMountService.Stub
Slog.w(TAG, "Waiting too long for mReady!");
}
}
-
+
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -344,12 +430,12 @@ class MountService extends IMountService.Stub
MountServiceBinderListener(IMountServiceListener listener) {
mListener = listener;
-
+
}
public void binderDied() {
if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
- synchronized(mListeners) {
+ synchronized (mListeners) {
mListeners.remove(this);
mListener.asBinder().unlinkToDeath(this, 0);
}
@@ -642,10 +728,21 @@ class MountService extends IMountService.Stub
}
private boolean doGetShareMethodAvailable(String method) {
- ArrayList<String> rsp = mConnector.doCommand("share status " + method);
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("share status " + method);
+ } catch (NativeDaemonConnectorException ex) {
+ Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
+ return false;
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ Slog.e(TAG, "Malformed response to share status " + method);
+ return false;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
@@ -770,10 +867,22 @@ 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 = mConnector.doCommand(cmd);
+ ArrayList<String> rsp;
+
+ try {
+ rsp = mConnector.doCommand(cmd);
+ } 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(" ");
+ 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]);
@@ -782,9 +891,7 @@ class MountService extends IMountService.Stub
return false;
}
if (code == VoldResponseCode.ShareEnabledResult) {
- if (tok[2].equals("enabled"))
- return true;
- return false;
+ return "enabled".equals(tok[2]);
} else {
Slog.e(TAG, String.format("Unexpected response code %d", code));
return false;
@@ -873,6 +980,9 @@ class MountService extends IMountService.Stub
mHandlerThread.start();
mHandler = new MountServiceHandler(mHandlerThread.getLooper());
+ // Add OBB Action Handler to MountService thread.
+ mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
+
/*
* Vold does not run in the simulator, so pretend the connector thread
* ran and did its thing.
@@ -1306,5 +1416,438 @@ class MountService extends IMountService.Stub
public void finishMediaUpdate() {
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
+
+ private boolean isCallerOwnerOfPackageOrSystem(String packageName) {
+ final int callerUid = Binder.getCallingUid();
+ return isUidOwnerOfPackageOrSystem(packageName, callerUid);
+ }
+
+ private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
+ if (callerUid == android.os.Process.SYSTEM_UID) {
+ return true;
+ }
+
+ if (packageName == null) {
+ return false;
+ }
+
+ final int packageUid = mPms.getPackageUid(packageName);
+
+ if (DEBUG_OBB) {
+ Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
+ packageUid + ", callerUid = " + callerUid);
+ }
+
+ return callerUid == packageUid;
+ }
+
+ public String getMountedObbPath(String filename) {
+ waitForReady();
+ warnOnNotMounted();
+
+ 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];
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageNotFound) {
+ return null;
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ }
+
+ public boolean isObbMounted(String filename) {
+ synchronized (mObbMounts) {
+ return mObbPathToStateMap.containsKey(filename);
+ }
+ }
+
+ public void mountObb(String filename, String key, IObbActionListener token) {
+ waitForReady();
+ warnOnNotMounted();
+
+ final ObbState obbState;
+
+ synchronized (mObbMounts) {
+ if (isObbMounted(filename)) {
+ throw new IllegalArgumentException("OBB file is already mounted");
+ }
+
+ if (mObbMounts.containsKey(token)) {
+ throw new IllegalArgumentException("You may only have one OBB mounted at a time");
+ }
+
+ final int callerUid = Binder.getCallingUid();
+ obbState = new ObbState(filename, token, callerUid);
+ addObbState(obbState);
+ }
+
+ try {
+ token.asBinder().linkToDeath(obbState, 0);
+ } catch (RemoteException rex) {
+ Slog.e(TAG, "Failed to link to listener death");
+ }
+
+ MountObbAction action = new MountObbAction(obbState, key);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
+ }
+
+ public void unmountObb(String filename, boolean force, IObbActionListener token) {
+ final ObbState obbState;
+
+ synchronized (mObbMounts) {
+ if (!isObbMounted(filename)) {
+ throw new IllegalArgumentException("OBB is not mounted");
+ }
+ obbState = mObbPathToStateMap.get(filename);
+ }
+
+ UnmountObbAction action = new UnmountObbAction(obbState, force);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
+ }
+
+ private void addObbState(ObbState obbState) {
+ synchronized (mObbMounts) {
+ mObbMounts.put(obbState.token, obbState);
+ mObbPathToStateMap.put(obbState.filename, obbState);
+ }
+ }
+
+ private void removeObbState(ObbState obbState) {
+ synchronized (mObbMounts) {
+ mObbMounts.remove(obbState.token);
+ mObbPathToStateMap.remove(obbState.filename);
+ }
+ }
+
+ private class ObbActionHandler extends Handler {
+ private boolean mBound = false;
+ private List<ObbAction> mActions = new LinkedList<ObbAction>();
+
+ ObbActionHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OBB_RUN_ACTION: {
+ ObbAction action = (ObbAction) msg.obj;
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
+
+ // If a bind was already initiated we don't really
+ // need to do anything. The pending install
+ // will be processed later on.
+ if (!mBound) {
+ // If this is the only one pending we might
+ // have to bind to the service again.
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ action.handleError();
+ return;
+ } else {
+ // Once we bind to the service, the first
+ // pending request will be processed.
+ mActions.add(action);
+ }
+ } else {
+ // Already bound to the service. Just make
+ // sure we trigger off processing the first request.
+ if (mActions.size() == 0) {
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+ }
+
+ mActions.add(action);
+ }
+ break;
+ }
+ case OBB_MCS_BOUND: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_BOUND");
+ if (msg.obj != null) {
+ mContainerService = (IMediaContainerService) msg.obj;
+ }
+ if (mContainerService == null) {
+ // Something seriously wrong. Bail out
+ Slog.e(TAG, "Cannot bind to media container service");
+ for (ObbAction action : mActions) {
+ // Indicate service bind error
+ action.handleError();
+ }
+ mActions.clear();
+ } else if (mActions.size() > 0) {
+ ObbAction action = mActions.get(0);
+ if (action != null) {
+ action.execute(this);
+ }
+ } else {
+ // Should never happen ideally.
+ Slog.w(TAG, "Empty queue");
+ }
+ break;
+ }
+ case OBB_MCS_RECONNECT: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_RECONNECT");
+ if (mActions.size() > 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ for (ObbAction action : mActions) {
+ // Indicate service bind error
+ action.handleError();
+ }
+ mActions.clear();
+ }
+ }
+ break;
+ }
+ case OBB_MCS_UNBIND: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_UNBIND");
+
+ // Delete pending install
+ if (mActions.size() > 0) {
+ mActions.remove(0);
+ }
+ if (mActions.size() == 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ } else {
+ // There are more pending requests in queue.
+ // Just post MCS_BOUND message to trigger processing
+ // of next pending install.
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+ }
+ break;
+ }
+ case OBB_MCS_GIVE_UP: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_GIVE_UP");
+ mActions.remove(0);
+ break;
+ }
+ }
+ }
+
+ private boolean connectToService() {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Trying to bind to DefaultContainerService");
+
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+ if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
+ mBound = true;
+ return true;
+ }
+ return false;
+ }
+
+ private void disconnectService() {
+ mContainerService = null;
+ mBound = false;
+ mContext.unbindService(mDefContainerConn);
+ }
+ }
+
+ abstract class ObbAction {
+ private static final int MAX_RETRIES = 3;
+ private int mRetries;
+
+ ObbState mObbState;
+
+ ObbAction(ObbState obbState) {
+ mObbState = obbState;
+ }
+
+ public void execute(ObbActionHandler handler) {
+ try {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Starting to execute action: " + this.toString());
+ mRetries++;
+ if (mRetries > MAX_RETRIES) {
+ Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_GIVE_UP);
+ handleError();
+ return;
+ } else {
+ handleExecute();
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Posting install MCS_UNBIND");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Posting install MCS_RECONNECT");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
+ } catch (Exception e) {
+ if (DEBUG_OBB)
+ Slog.d(TAG, "Error handling OBB action", e);
+ handleError();
+ }
+ }
+
+ abstract void handleExecute() throws RemoteException;
+ abstract void handleError();
+ }
+
+ class MountObbAction extends ObbAction {
+ private String mKey;
+
+ MountObbAction(ObbState obbState, String key) {
+ super(obbState);
+ mKey = key;
+ }
+
+ public void handleExecute() throws RemoteException {
+ ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+ if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ if (mKey == null) {
+ mKey = "none";
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
+ mObbState.callerUid);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code != VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "mounted");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ } else {
+ Slog.e(TAG, "Couldn't mount OBB file");
+
+ // We didn't succeed, so remove this from the mount-set.
+ removeObbState(mObbState);
+ }
+ }
+
+ public void handleError() {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("MountObbAction{");
+ sb.append("filename=");
+ sb.append(mObbState.filename);
+ sb.append(",callerUid=");
+ sb.append(mObbState.callerUid);
+ sb.append(",token=");
+ sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ class UnmountObbAction extends ObbAction {
+ private boolean mForceUnmount;
+
+ UnmountObbAction(ObbState obbState, boolean force) {
+ super(obbState);
+ mForceUnmount = force;
+ }
+
+ public void handleExecute() throws RemoteException {
+ ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+
+ if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb unmount %s%s", mObbState.filename,
+ (mForceUnmount ? " force" : ""));
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedStorageBusy;
+ } else {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "unmounted");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ } else {
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ }
+ }
+
+ public void handleError() {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("UnmountObbAction{");
+ sb.append("filename=");
+ sb.append(mObbState.filename != null ? mObbState.filename : "null");
+ sb.append(",force=");
+ sb.append(mForceUnmount);
+ sb.append(",callerUid=");
+ sb.append(mObbState.callerUid);
+ sb.append(",token=");
+ sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
+ sb.append('}');
+ return sb.toString();
+ }
+ }
}
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 08d7ce6..c452590 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -128,12 +128,11 @@ final class NativeDaemonConnector implements Runnable {
Slog.e(TAG, String.format(
"Error handling '%s'", event), ex);
}
- } else {
- try {
- mResponseQueue.put(event);
- } catch (InterruptedException ex) {
- Slog.e(TAG, "Failed to put response onto queue", ex);
- }
+ }
+ try {
+ mResponseQueue.put(event);
+ } catch (InterruptedException ex) {
+ Slog.e(TAG, "Failed to put response onto queue", ex);
}
} catch (NumberFormatException nfe) {
Slog.w(TAG, String.format("Bad msg (%s)", event));
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index cbbc7be..c156150 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -35,6 +35,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
+import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import android.provider.Settings;
import android.content.ContentResolver;
@@ -226,44 +227,61 @@ class NetworkManagementService extends INetworkManagementService.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+ try {
+ return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Cannot communicate with native daemon to list interfaces");
+ }
}
public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
- String rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+ String rsp;
+ try {
+ rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Cannot communicate with native daemon to get interface config");
+ }
Slog.d(TAG, String.format("rsp <%s>", rsp));
// Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
StringTokenizer st = new StringTokenizer(rsp);
+ InterfaceConfiguration cfg;
try {
- int code = Integer.parseInt(st.nextToken(" "));
- if (code != NetdResponseCode.InterfaceGetCfgResult) {
+ 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("Expected code %d, but got %d",
- NetdResponseCode.InterfaceGetCfgResult, code));
+ String.format("Invalid response from daemon (%s)", rsp));
}
- } catch (NumberFormatException nfe) {
- throw new IllegalStateException(
- String.format("Invalid response from daemon (%s)", rsp));
- }
- InterfaceConfiguration cfg = new InterfaceConfiguration();
- cfg.hwAddr = st.nextToken(" ");
- try {
- cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
- } catch (UnknownHostException uhe) {
- Slog.e(TAG, "Failed to parse ipaddr", uhe);
- cfg.ipAddr = 0;
- }
+ cfg = new InterfaceConfiguration();
+ cfg.hwAddr = st.nextToken(" ");
+ try {
+ cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
+ } catch (UnknownHostException uhe) {
+ Slog.e(TAG, "Failed to parse ipaddr", uhe);
+ cfg.ipAddr = 0;
+ }
- try {
- cfg.netmask = stringToIpAddr(st.nextToken(" "));
- } catch (UnknownHostException uhe) {
- Slog.e(TAG, "Failed to parse netmask", uhe);
- cfg.netmask = 0;
+ try {
+ cfg.netmask = stringToIpAddr(st.nextToken(" "));
+ } catch (UnknownHostException uhe) {
+ Slog.e(TAG, "Failed to parse netmask", uhe);
+ cfg.netmask = 0;
+ }
+ cfg.interfaceFlags = st.nextToken("]").trim() +"]";
+ } catch (NoSuchElementException nsee) {
+ throw new IllegalStateException(
+ String.format("Invalid response from daemon (%s)", rsp));
}
- cfg.interfaceFlags = st.nextToken("]").trim() +"]";
Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
return cfg;
}
@@ -272,7 +290,12 @@ class NetworkManagementService extends INetworkManagementService.Stub {
String iface, InterfaceConfiguration cfg) throws IllegalStateException {
String cmd = String.format("interface setcfg %s %s %s %s", iface,
intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
- mConnector.doCommand(cmd);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate with native daemon to interface setcfg");
+ }
}
public void shutdown() {
@@ -289,20 +312,25 @@ class NetworkManagementService extends INetworkManagementService.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("ipfwd status");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate with native daemon to ipfwd status");
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ 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>
- if (tok.length !=2) {
- throw new IllegalStateException(
- String.format("Malformatted list entry '%s'", line));
- }
- if (tok[2].equals("enabled"))
- return true;
- return false;
+ return "enabled".equals(tok[2]);
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
@@ -326,29 +354,45 @@ class NetworkManagementService extends INetworkManagementService.Stub {
for (String d : dhcpRange) {
cmd += " " + d;
}
- mConnector.doCommand(cmd);
+
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Unable to communicate to native daemon");
+ }
}
public void stopTethering() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether stop");
+ try {
+ mConnector.doCommand("tether stop");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
+ }
}
public boolean isTetheringStarted() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("tether status");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("tether status");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon to get tether status");
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ 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>...
- if (tok[2].equals("started"))
- return true;
- return false;
+ return "started".equals(tok[2]);
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
@@ -359,20 +403,35 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public void tetherInterface(String iface) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether interface add " + iface);
+ try {
+ mConnector.doCommand("tether interface add " + iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for adding tether interface");
+ }
}
public void untetherInterface(String iface) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether interface remove " + iface);
+ try {
+ mConnector.doCommand("tether interface remove " + iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for removing tether interface");
+ }
}
public String[] listTetheredInterfaces() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand(
- "tether interface list", NetdResponseCode.TetherInterfaceListResult);
+ try {
+ return mConnector.doListCommand(
+ "tether interface list", NetdResponseCode.TetherInterfaceListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing tether interfaces");
+ }
}
public void setDnsForwarders(String[] dns) throws IllegalStateException {
@@ -383,7 +442,12 @@ class NetworkManagementService extends INetworkManagementService.Stub {
for (String s : dns) {
cmd += " " + InetAddress.getByName(s).getHostAddress();
}
- mConnector.doCommand(cmd);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for setting tether dns");
+ }
} catch (UnknownHostException e) {
throw new IllegalStateException("Error resolving dns name", e);
}
@@ -392,30 +456,50 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public String[] getDnsForwarders() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand(
- "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
+ try {
+ return mConnector.doListCommand(
+ "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing tether dns");
+ }
}
public void enableNat(String internalInterface, String externalInterface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(
- String.format("nat enable %s %s", internalInterface, externalInterface));
+ try {
+ mConnector.doCommand(
+ String.format("nat enable %s %s", internalInterface, externalInterface));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for enabling NAT interface");
+ }
}
public void disableNat(String internalInterface, String externalInterface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(
- String.format("nat disable %s %s", internalInterface, externalInterface));
+ try {
+ mConnector.doCommand(
+ String.format("nat disable %s %s", internalInterface, externalInterface));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for disabling NAT interface");
+ }
}
public String[] listTtys() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
+ try {
+ return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing TTYs");
+ }
}
public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
@@ -430,31 +514,52 @@ class NetworkManagementService extends INetworkManagementService.Stub {
InetAddress.getByName(dns2Addr).getHostAddress()));
} catch (UnknownHostException e) {
throw new IllegalStateException("Error resolving addr", e);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
}
}
public void detachPppd(String tty) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("pppd detach %s", tty));
+ try {
+ mConnector.doCommand(String.format("pppd detach %s", tty));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
+ }
}
public void startUsbRNDIS() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("usb startrndis");
+ try {
+ mConnector.doCommand("usb startrndis");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating to native daemon for starting RNDIS", e);
+ }
}
public void stopUsbRNDIS() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("usb stoprndis");
+ try {
+ mConnector.doCommand("usb stoprndis");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
}
public boolean isUsbRNDISStarted() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("usb rndisstatus");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating to native daemon to check RNDIS status", e);
+ }
for (String line : rsp) {
String []tok = line.split(" ");
@@ -476,31 +581,35 @@ class NetworkManagementService extends INetworkManagementService.Stub {
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("softap stop " + wlanIface));
- mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
- mConnector.doCommand(String.format("softap start " + wlanIface));
- if (wifiConfig == null) {
- mConnector.doCommand(String.format("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),
- wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
- "wpa2-psk" : "open",
- convertQuotedString(wifiConfig.preSharedKey));
- mConnector.doCommand(str);
- }
- mConnector.doCommand(String.format("softap startap"));
+ try {
+ mConnector.doCommand(String.format("softap stop " + wlanIface));
+ mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
+ mConnector.doCommand(String.format("softap start " + wlanIface));
+ if (wifiConfig == null) {
+ mConnector.doCommand(String.format("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),
+ wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
+ "wpa2-psk" : "open",
+ convertQuotedString(wifiConfig.preSharedKey));
+ mConnector.doCommand(str);
+ }
+ mConnector.doCommand(String.format("softap startap"));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to start softap", e);
+ }
}
private String convertQuotedString(String s) {
@@ -516,7 +625,12 @@ class NetworkManagementService extends INetworkManagementService.Stub {
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- mConnector.doCommand("softap stopap");
+ try {
+ mConnector.doCommand("softap stopap");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
+ e);
+ }
}
public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
@@ -525,15 +639,19 @@ class NetworkManagementService extends INetworkManagementService.Stub {
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- if (wifiConfig == null) {
- mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
- } else {
- String str = String.format("softap set " + wlanIface + " " + softapIface +
- " %s %s %s", convertQuotedString(wifiConfig.SSID),
- wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
- "wpa2-psk" : "open",
- convertQuotedString(wifiConfig.preSharedKey));
- mConnector.doCommand(str);
+ try {
+ if (wifiConfig == null) {
+ mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ } else {
+ String str = String.format("softap set " + wlanIface + " " + softapIface
+ + " %s %s %s", convertQuotedString(wifiConfig.SSID),
+ wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open",
+ convertQuotedString(wifiConfig.preSharedKey));
+ mConnector.doCommand(str);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to set soft AP",
+ e);
}
}
@@ -541,9 +659,22 @@ class NetworkManagementService extends INetworkManagementService.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
try {
- String rsp = mConnector.doCommand(
- String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
- String []tok = rsp.split(" ");
+ 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]);
@@ -575,17 +706,34 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format(
- "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
+ try {
+ mConnector.doCommand(String.format(
+ "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
+ }
}
private int getInterfaceThrottle(String iface, boolean rx) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
try {
- String rsp = mConnector.doCommand(
- String.format("interface getthrottle %s %s", iface,(rx ? "rx" : "tx"))).get(0);
- String []tok = rsp.split(" ");
+ 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;
+ }
+
+ String[] tok = rsp.split(" ");
+ if (tok.length < 2) {
+ Slog.e(TAG, "Malformed response to getthrottle command");
+ return -1;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 348accd..ed8b90b 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -16,9 +16,8 @@
package com.android.server;
-import com.android.server.status.IconData;
-import com.android.server.status.NotificationData;
-import com.android.server.status.StatusBarService;
+import com.android.internal.statusbar.StatusBarNotification;
+import com.android.server.StatusBarManagerService;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
@@ -39,9 +38,11 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.hardware.Usb;
import android.media.AudioManager;
import android.net.Uri;
import android.os.BatteryManager;
+import android.os.Bundle;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -66,11 +67,14 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-class NotificationManagerService extends INotificationManager.Stub
+/** {@hide} */
+public class NotificationManagerService extends INotificationManager.Stub
{
private static final String TAG = "NotificationService";
private static final boolean DBG = false;
+ private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
+
// message codes
private static final int MESSAGE_TIMEOUT = 2;
@@ -86,7 +90,7 @@ class NotificationManagerService extends INotificationManager.Stub
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
- private StatusBarService mStatusBarService;
+ private StatusBarManagerService mStatusBar;
private LightsService mLightsService;
private LightsService.Light mBatteryLight;
private LightsService.Light mNotificationLight;
@@ -110,8 +114,6 @@ class NotificationManagerService extends INotificationManager.Stub
private boolean mNotificationPulseEnabled;
// for adb connected notifications
- private boolean mUsbConnected;
- private boolean mAdbEnabled = false;
private boolean mAdbNotificationShown = false;
private Notification mAdbNotification;
@@ -163,16 +165,21 @@ class NotificationManagerService extends INotificationManager.Stub
final String pkg;
final String tag;
final int id;
+ final int uid;
+ final int initialPid;
ITransientNotification callback;
int duration;
final Notification notification;
IBinder statusBarKey;
- NotificationRecord(String pkg, String tag, int id, Notification notification)
+ NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
+ Notification notification)
{
this.pkg = pkg;
this.tag = tag;
this.id = id;
+ this.uid = uid;
+ this.initialPid = initialPid;
this.notification = notification;
}
@@ -238,8 +245,8 @@ class NotificationManagerService extends INotificationManager.Stub
}
}
- private StatusBarService.NotificationCallbacks mNotificationCallbacks
- = new StatusBarService.NotificationCallbacks() {
+ private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
+ = new StatusBarManagerService.NotificationCallbacks() {
public void onSetDisabled(int status) {
synchronized (mNotificationList) {
@@ -302,6 +309,21 @@ class NotificationManagerService extends INotificationManager.Stub
updateLightsLocked();
}
}
+
+ public void onNotificationError(String pkg, String tag, int id,
+ int uid, int initialPid, String message) {
+ Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
+ + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
+ cancelNotification(pkg, tag, id, 0, 0);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
+ "Bad notification posted from package " + pkg
+ + ": " + message);
+ } catch (RemoteException e) {
+ }
+ Binder.restoreCallingIdentity(ident);
+ }
};
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -326,12 +348,14 @@ class NotificationManagerService extends INotificationManager.Stub
mBatteryFull = batteryFull;
updateLights();
}
- } else if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
- mUsbConnected = true;
- updateAdbNotification();
- } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
- mUsbConnected = false;
- updateAdbNotification();
+ } else if (action.equals(Usb.ACTION_USB_STATE)) {
+ Bundle extras = intent.getExtras();
+ boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED);
+ boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals(
+ extras.getString(Usb.USB_FUNCTION_ADB)));
+ updateAdbNotification(usbConnected && adbEnabled);
+ } else if (action.equals(Usb.ACTION_USB_DISCONNECTED)) {
+ updateAdbNotification(false);
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)
|| (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
@@ -377,8 +401,6 @@ class NotificationManagerService extends INotificationManager.Stub
void observe() {
ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ADB_ENABLED), false, this);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
update();
@@ -390,12 +412,6 @@ class NotificationManagerService extends INotificationManager.Stub
public void update() {
ContentResolver resolver = mContext.getContentResolver();
- boolean adbEnabled = Settings.Secure.getInt(resolver,
- Settings.Secure.ADB_ENABLED, 0) != 0;
- if (mAdbEnabled != adbEnabled) {
- mAdbEnabled = adbEnabled;
- updateAdbNotification();
- }
boolean pulseEnabled = Settings.System.getInt(resolver,
Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
if (mNotificationPulseEnabled != pulseEnabled) {
@@ -405,7 +421,7 @@ class NotificationManagerService extends INotificationManager.Stub
}
}
- NotificationManagerService(Context context, StatusBarService statusBar,
+ NotificationManagerService(Context context, StatusBarManagerService statusBar,
LightsService lights)
{
super();
@@ -417,7 +433,7 @@ class NotificationManagerService extends INotificationManager.Stub
mToastQueue = new ArrayList<ToastRecord>();
mHandler = new WorkerHandler();
- mStatusBarService = statusBar;
+ mStatusBar = statusBar;
statusBar.setNotificationCallbacks(mNotificationCallbacks);
mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
@@ -455,8 +471,7 @@ class NotificationManagerService extends INotificationManager.Stub
// register for battery changed notifications
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_UMS_CONNECTED);
- filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
+ filter.addAction(Usb.ACTION_USB_STATE);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
@@ -664,11 +679,43 @@ class NotificationManagerService extends INotificationManager.Stub
enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
}
- public void enqueueNotificationWithTag(String pkg, String tag, int id,
- Notification notification, int[] idOut)
+ public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
+ int[] idOut)
+ {
+ enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
+ tag, id, notification, idOut);
+ }
+
+ // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
+ // uid/pid of another application)
+ public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
+ String tag, int id, Notification notification, int[] idOut)
{
+ Slog.d(TAG, "enqueueNotificationWithTag: calling uid=" + callingUid
+ + ", pid=" + callingPid);
+
checkIncomingCall(pkg);
+ // Limit the number of notifications that any given package except the android
+ // package can enqueue. Prevents DOS attacks and deals with leaks.
+ if (!"android".equals(pkg)) {
+ synchronized (mNotificationList) {
+ int count = 0;
+ final int N = mNotificationList.size();
+ for (int i=0; i<N; i++) {
+ final NotificationRecord r = mNotificationList.get(i);
+ if (r.pkg.equals(pkg)) {
+ count++;
+ if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+ Slog.e(TAG, "Package has already posted " + count
+ + " notifications. Not showing more. package=" + pkg);
+ return;
+ }
+ }
+ }
+ }
+ }
+
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
if (!pkg.equals("com.android.providers.downloads")
@@ -692,7 +739,8 @@ class NotificationManagerService extends INotificationManager.Stub
}
synchronized (mNotificationList) {
- NotificationRecord r = new NotificationRecord(pkg, tag, id, notification);
+ NotificationRecord r = new NotificationRecord(pkg, tag, id,
+ callingUid, callingPid, notification);
NotificationRecord old = null;
int index = indexOfNotificationLocked(pkg, tag, id);
@@ -716,36 +764,13 @@ class NotificationManagerService extends INotificationManager.Stub
}
if (notification.icon != 0) {
- IconData icon = IconData.makeIcon(null, pkg, notification.icon,
- notification.iconLevel,
- notification.number);
- CharSequence truncatedTicker = notification.tickerText;
-
- // TODO: make this restriction do something smarter like never fill
- // more than two screens. "Why would anyone need more than 80 characters." :-/
- final int maxTickerLen = 80;
- if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) {
- truncatedTicker = truncatedTicker.subSequence(0, maxTickerLen);
- }
-
- NotificationData n = new NotificationData();
- n.pkg = pkg;
- n.tag = tag;
- n.id = id;
- n.when = notification.when;
- n.tickerText = truncatedTicker;
- n.ongoingEvent = (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
- if (!n.ongoingEvent && (notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
- n.clearable = true;
- }
- n.contentView = notification.contentView;
- n.contentIntent = notification.contentIntent;
- n.deleteIntent = notification.deleteIntent;
+ StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
+ r.uid, r.initialPid, notification);
if (old != null && old.statusBarKey != null) {
r.statusBarKey = old.statusBarKey;
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.updateIcon(r.statusBarKey, icon, n);
+ mStatusBar.updateNotification(r.statusBarKey, n);
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -753,21 +778,19 @@ class NotificationManagerService extends INotificationManager.Stub
} else {
long identity = Binder.clearCallingIdentity();
try {
- r.statusBarKey = mStatusBarService.addIcon(icon, n);
+ r.statusBarKey = mStatusBar.addNotification(n);
mAttentionLight.pulse();
}
finally {
Binder.restoreCallingIdentity(identity);
}
}
-
sendAccessibilityEvent(notification, pkg);
-
} else {
if (old != null && old.statusBarKey != null) {
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.removeIcon(old.statusBarKey);
+ mStatusBar.removeNotification(old.statusBarKey);
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -875,7 +898,7 @@ class NotificationManagerService extends INotificationManager.Stub
if (r.notification.icon != 0) {
long identity = Binder.clearCallingIdentity();
try {
- mStatusBarService.removeIcon(r.statusBarKey);
+ mStatusBar.removeNotification(r.statusBarKey);
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -1129,8 +1152,8 @@ class NotificationManagerService extends INotificationManager.Stub
// This is here instead of StatusBarPolicy because it is an important
// security feature that we don't want people customizing the platform
// to accidentally lose.
- private void updateAdbNotification() {
- if (mAdbEnabled && mUsbConnected) {
+ private void updateAdbNotification(boolean adbEnabled) {
+ if (adbEnabled) {
if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
return;
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index d23c16a..ab50ba7 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -18,6 +18,7 @@ package com.android.server;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
+import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
@@ -112,6 +113,7 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -150,6 +152,8 @@ class PackageManagerService extends IPackageManager.Stub {
private static final boolean GET_CERTIFICATES = true;
+ private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
+
private static final int REMOVE_EVENTS =
FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM;
private static final int ADD_EVENTS =
@@ -185,7 +189,11 @@ class PackageManagerService extends IPackageManager.Stub {
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
"com.android.defcontainer",
"com.android.defcontainer.DefaultContainerService");
-
+
+ private static final String LIB_DIR_NAME = "lib";
+
+ static final String mTempContainerPrefix = "smdl2tmp";
+
final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
Process.THREAD_PRIORITY_BACKGROUND);
final PackageHandler mHandler;
@@ -204,6 +212,10 @@ class PackageManagerService extends IPackageManager.Stub {
// This is where all application persistent data goes.
final File mAppDataDir;
+ // If Encrypted File System feature is enabled, all application persistent data
+ // should go here instead.
+ final File mSecureAppDataDir;
+
// This is the object monitoring the framework dir.
final FileObserver mFrameworkInstallObserver;
@@ -670,13 +682,6 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
- static boolean isFwdLocked(int flags) {
- if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
- return true;
- }
- return false;
- }
-
public static final IPackageManager main(Context context, boolean factoryTest) {
PackageManagerService m = new PackageManagerService(context, factoryTest);
ServiceManager.addService("package", m);
@@ -768,6 +773,7 @@ class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mSecureAppDataDir = new File(dataDir, "secure/data");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
if (mInstaller == null) {
@@ -777,6 +783,7 @@ class PackageManagerService extends IPackageManager.Stub {
File miscDir = new File(dataDir, "misc");
miscDir.mkdirs();
mAppDataDir.mkdirs();
+ mSecureAppDataDir.mkdirs();
mDrmAppPrivateInstallDir.mkdirs();
}
@@ -937,7 +944,9 @@ class PackageManagerService extends IPackageManager.Stub {
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
if (mInstaller != null) {
- mInstaller.remove(ps.name);
+ // XXX how to set useEncryptedFSDir for packages that
+ // are not encrypted?
+ mInstaller.remove(ps.name, true);
}
}
}
@@ -1020,7 +1029,8 @@ class PackageManagerService extends IPackageManager.Stub {
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
if (mInstaller != null) {
- int retCode = mInstaller.remove(ps.name);
+ boolean useSecureFS = useEncryptedFilesystemForPackage(ps.pkg);
+ int retCode = mInstaller.remove(ps.name, useSecureFS);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
@@ -1484,6 +1494,8 @@ class PackageManagerService extends IPackageManager.Stub {
ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
ps.pkg.applicationInfo.sourceDir = ps.codePathString;
ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath();
+ ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
+ ps.pkg.mSetEnabled = ps.enabled;
}
return generatePackageInfo(ps.pkg, flags);
}
@@ -1718,6 +1730,7 @@ class PackageManagerService extends IPackageManager.Stub {
static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
if (pi1.icon != pi2.icon) return false;
+ if (pi1.logo != pi2.logo) return false;
if (pi1.protectionLevel != pi2.protectionLevel) return false;
if (!compareStrings(pi1.name, pi2.name)) return false;
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
@@ -2361,8 +2374,7 @@ class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
- && (!mSafeMode || (p.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) != 0)) {
+ && (!mSafeMode || isSystemApp(p))) {
finalList.add(PackageParser.generateApplicationInfo(p, flags));
}
}
@@ -2606,9 +2618,9 @@ class PackageManagerService extends IPackageManager.Stub {
+ "reverting from " + ps.codePathString
+ ": new version " + pkg.mVersionCode
+ " better than installed " + ps.versionCode);
- InstallArgs args = new FileInstallArgs(ps.codePathString, ps.resourcePathString);
+ InstallArgs args = new FileInstallArgs(ps.codePathString,
+ ps.resourcePathString, ps.nativeLibraryPathString);
args.cleanUpResourcesLI();
- removeNativeBinariesLI(pkg);
mSettings.enableSystemPackageLP(ps.name);
}
}
@@ -2648,8 +2660,8 @@ class PackageManagerService extends IPackageManager.Stub {
return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
}
- private static void setApplicationInfoPaths(PackageParser.Package pkg,
- String destCodePath, String destResPath) {
+ private static void setApplicationInfoPaths(PackageParser.Package pkg, String destCodePath,
+ String destResPath) {
pkg.mPath = pkg.mScanPath = destCodePath;
pkg.applicationInfo.sourceDir = destCodePath;
pkg.applicationInfo.publicSourceDir = destResPath;
@@ -2743,6 +2755,11 @@ class PackageManagerService extends IPackageManager.Stub {
return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
}
+
+ private static boolean useEncryptedFilesystemForPackage(PackageParser.Package pkg) {
+ return Environment.isEncryptedFilesystemEnabled() &&
+ ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_NEVER_ENCRYPT) == 0);
+ }
private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) {
if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -2760,7 +2777,14 @@ class PackageManagerService extends IPackageManager.Stub {
}
private File getDataPathForPackage(PackageParser.Package pkg) {
- return new File(mAppDataDir, pkg.packageName);
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
+ File dataPath;
+ if (useEncryptedFSDir) {
+ dataPath = new File(mSecureAppDataDir, pkg.packageName);
+ } else {
+ dataPath = new File(mAppDataDir, pkg.packageName);
+ }
+ return dataPath;
}
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
@@ -3111,6 +3135,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
// This is a normal package, need to make its data directory.
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
dataPath = getDataPathForPackage(pkg);
boolean uidError = false;
@@ -3118,16 +3143,16 @@ class PackageManagerService extends IPackageManager.Stub {
if (dataPath.exists()) {
mOutPermissions[1] = 0;
FileUtils.getPermissions(dataPath.getPath(), mOutPermissions);
- if (mOutPermissions[1] == pkg.applicationInfo.uid
- || !Process.supportsProcesses()) {
- pkg.applicationInfo.dataDir = dataPath.getPath();
- } else {
+
+ // If we have mismatched owners for the data path, we have a
+ // problem (unless we're running in the simulator.)
+ if (mOutPermissions[1] != pkg.applicationInfo.uid && Process.supportsProcesses()) {
boolean recovered = false;
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
// If this is a system app, we can at least delete its
// current data so the application will still work.
if (mInstaller != null) {
- int ret = mInstaller.remove(pkgName);
+ int ret = mInstaller.remove(pkgName, useEncryptedFSDir);
if (ret >= 0) {
// Old data gone!
String msg = "System package " + pkg.packageName
@@ -3138,7 +3163,7 @@ class PackageManagerService extends IPackageManager.Stub {
recovered = true;
// And now re-install the app.
- ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+ ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
if (ret == -1) {
// Ack should not happen!
@@ -3158,6 +3183,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
+ pkg.applicationInfo.uid + "/fs_"
+ mOutPermissions[1];
+ pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
String msg = "Package " + pkg.packageName
+ " has mismatched uid: "
+ mOutPermissions[1] + " on disk, "
@@ -3178,7 +3204,7 @@ class PackageManagerService extends IPackageManager.Stub {
Log.v(TAG, "Want this data dir: " + dataPath);
//invoke installer to do the actual installation
if (mInstaller != null) {
- int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+ int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
if(ret < 0) {
// Error from installer
@@ -3201,32 +3227,38 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = null;
}
}
-
+
+ /*
+ * Set the data dir to the default "/data/data/<package name>/lib"
+ * if we got here without anyone telling us different (e.g., apps
+ * stored on SD card have their native libraries stored in the ASEC
+ * container with the APK).
+ */
+ if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) {
+ pkg.applicationInfo.nativeLibraryDir = new File(dataPath, LIB_DIR_NAME).getPath();
+ }
+
pkgSetting.uidError = uidError;
}
- // Perform shared library installation and dex validation and
- // optimization, if this is not a system app.
+ // If we're running in the simulator, we don't need to unpack anything.
if (mInstaller != null) {
String path = scanFile.getPath();
- if (scanFileNewer) {
- // Note: We don't want to unpack the native binaries for
- // system applications, unless they have been updated
- // (the binaries are already under /system/lib).
- //
- // In other words, we're going to unpack the binaries
- // only for non-system apps and system app upgrades.
- //
- int flags = pkg.applicationInfo.flags;
- if ((flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
- (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
- Log.i(TAG, path + " changed; unpacking");
- int err = cachePackageSharedLibsLI(pkg, scanFile);
- if (err != PackageManager.INSTALL_SUCCEEDED) {
- mLastScanError = err;
- return null;
- }
- }
+ /* Note: We don't want to unpack the native binaries for
+ * system applications, unless they have been updated
+ * (the binaries are already under /system/lib).
+ * Also, don't unpack libs for apps on the external card
+ * since they should have their libraries in the ASEC
+ * container already.
+ *
+ * In other words, we're going to unpack the binaries
+ * only for non-system apps and system app upgrades.
+ */
+ if ((!isSystemApp(pkg) || isUpdatedSystemApp(pkg)) && !isExternal(pkg)) {
+ Log.i(TAG, path + " changed; unpacking");
+ File sharedLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
+ sharedLibraryDir.mkdir();
+ NativeLibraryHelper.copyNativeBinariesLI(scanFile, sharedLibraryDir);
}
pkg.mScanPath = path;
@@ -3489,6 +3521,7 @@ class PackageManagerService extends IPackageManager.Stub {
a.info.sourceDir = pkg.applicationInfo.sourceDir;
a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
a.info.dataDir = pkg.applicationInfo.dataDir;
+ a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
mInstrumentation.put(a.getComponentName(), a);
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
@@ -3529,260 +3562,30 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- // The following constants are returned by cachePackageSharedLibsForAbiLI
- // to indicate if native shared libraries were found in the package.
- // Values are:
- // PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES => native libraries found and installed
- // PACKAGE_INSTALL_NATIVE_NO_LIBRARIES => no native libraries in package
- // PACKAGE_INSTALL_NATIVE_ABI_MISMATCH => native libraries for another ABI found
- // in package (and not installed)
- //
- private static final int PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES = 0;
- private static final int PACKAGE_INSTALL_NATIVE_NO_LIBRARIES = 1;
- private static final int PACKAGE_INSTALL_NATIVE_ABI_MISMATCH = 2;
-
// Return the path of the directory that will contain the native binaries
// of a given installed package. This is relative to the data path.
//
- private static File getNativeBinaryDirForPackage(PackageParser.Package pkg) {
- return new File(pkg.applicationInfo.dataDir + "/lib");
- }
-
- // Find all files of the form lib/<cpuAbi>/lib<name>.so in the .apk
- // and automatically copy them to /data/data/<appname>/lib if present.
- //
- // NOTE: this method may throw an IOException if the library cannot
- // be copied to its final destination, e.g. if there isn't enough
- // room left on the data partition, or a ZipException if the package
- // file is malformed.
- //
- private int cachePackageSharedLibsForAbiLI(PackageParser.Package pkg,
- File scanFile, String cpuAbi) throws IOException, ZipException {
- File sharedLibraryDir = getNativeBinaryDirForPackage(pkg);
- final String apkLib = "lib/";
- final int apkLibLen = apkLib.length();
- final int cpuAbiLen = cpuAbi.length();
- final String libPrefix = "lib";
- final int libPrefixLen = libPrefix.length();
- final String libSuffix = ".so";
- final int libSuffixLen = libSuffix.length();
- boolean hasNativeLibraries = false;
- boolean installedNativeLibraries = false;
-
- // the minimum length of a valid native shared library of the form
- // lib/<something>/lib<name>.so.
- final int minEntryLen = apkLibLen + 2 + libPrefixLen + 1 + libSuffixLen;
-
- ZipFile zipFile = new ZipFile(scanFile);
- Enumeration<ZipEntry> entries =
- (Enumeration<ZipEntry>) zipFile.entries();
-
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- // skip directories
- if (entry.isDirectory()) {
- continue;
- }
- String entryName = entry.getName();
-
- // check that the entry looks like lib/<something>/lib<name>.so
- // here, but don't check the ABI just yet.
- //
- // - must be sufficiently long
- // - must end with libSuffix, i.e. ".so"
- // - must start with apkLib, i.e. "lib/"
- if (entryName.length() < minEntryLen ||
- !entryName.endsWith(libSuffix) ||
- !entryName.startsWith(apkLib) ) {
- continue;
- }
-
- // file name must start with libPrefix, i.e. "lib"
- int lastSlash = entryName.lastIndexOf('/');
-
- if (lastSlash < 0 ||
- !entryName.regionMatches(lastSlash+1, libPrefix, 0, libPrefixLen) ) {
- continue;
- }
-
- hasNativeLibraries = true;
-
- // check the cpuAbi now, between lib/ and /lib<name>.so
- //
- if (lastSlash != apkLibLen + cpuAbiLen ||
- !entryName.regionMatches(apkLibLen, cpuAbi, 0, cpuAbiLen) )
- continue;
-
- // extract the library file name, ensure it doesn't contain
- // weird characters. we're guaranteed here that it doesn't contain
- // a directory separator though.
- String libFileName = entryName.substring(lastSlash+1);
- if (!FileUtils.isFilenameSafe(new File(libFileName))) {
- continue;
- }
-
- installedNativeLibraries = true;
-
- String sharedLibraryFilePath = sharedLibraryDir.getPath() +
- File.separator + libFileName;
- File sharedLibraryFile = new File(sharedLibraryFilePath);
- if (! sharedLibraryFile.exists() ||
- sharedLibraryFile.length() != entry.getSize() ||
- sharedLibraryFile.lastModified() != entry.getTime()) {
- if (Config.LOGD) {
- Log.d(TAG, "Caching shared lib " + entry.getName());
- }
- if (mInstaller == null) {
- sharedLibraryDir.mkdir();
- }
- cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir,
- sharedLibraryFile);
- }
- }
- if (!hasNativeLibraries)
- return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
-
- if (!installedNativeLibraries)
- return PACKAGE_INSTALL_NATIVE_ABI_MISMATCH;
-
- return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
- }
-
- // Find the gdbserver executable program in a package at
- // lib/<cpuAbi>/gdbserver and copy it to /data/data/<name>/lib/gdbserver
- //
- // Returns PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES on success,
- // or PACKAGE_INSTALL_NATIVE_NO_LIBRARIES otherwise.
- //
- private int cachePackageGdbServerLI(PackageParser.Package pkg,
- File scanFile, String cpuAbi) throws IOException, ZipException {
- File installGdbServerDir = getNativeBinaryDirForPackage(pkg);
- final String GDBSERVER = "gdbserver";
- final String apkGdbServerPath = "lib/" + cpuAbi + "/" + GDBSERVER;
-
- ZipFile zipFile = new ZipFile(scanFile);
- Enumeration<ZipEntry> entries =
- (Enumeration<ZipEntry>) zipFile.entries();
-
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- // skip directories
- if (entry.isDirectory()) {
- continue;
- }
- String entryName = entry.getName();
-
- if (!entryName.equals(apkGdbServerPath)) {
- continue;
- }
-
- String installGdbServerPath = installGdbServerDir.getPath() +
- "/" + GDBSERVER;
- File installGdbServerFile = new File(installGdbServerPath);
- if (! installGdbServerFile.exists() ||
- installGdbServerFile.length() != entry.getSize() ||
- installGdbServerFile.lastModified() != entry.getTime()) {
- if (Config.LOGD) {
- Log.d(TAG, "Caching gdbserver " + entry.getName());
- }
- if (mInstaller == null) {
- installGdbServerDir.mkdir();
- }
- cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir,
- installGdbServerFile);
- }
- return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
- }
- return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
- }
-
- // extract shared libraries stored in the APK as lib/<cpuAbi>/lib<name>.so
- // and copy them to /data/data/<appname>/lib.
- //
- // This function will first try the main CPU ABI defined by Build.CPU_ABI
- // (which corresponds to ro.product.cpu.abi), and also try an alternate
- // one if ro.product.cpu.abi2 is defined.
- //
- private int cachePackageSharedLibsLI(PackageParser.Package pkg, File scanFile) {
- String cpuAbi = Build.CPU_ABI;
- try {
- int result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi);
-
- // some architectures are capable of supporting several CPU ABIs
- // for example, 'armeabi-v7a' also supports 'armeabi' native code
- // this is indicated by the definition of the ro.product.cpu.abi2
- // system property.
- //
- // only scan the package twice in case of ABI mismatch
- if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
- final String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2",null);
- if (cpuAbi2 != null) {
- result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi2);
- }
-
- if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) {
- Slog.w(TAG,"Native ABI mismatch from package file");
- return PackageManager.INSTALL_FAILED_INVALID_APK;
- }
-
- if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) {
- cpuAbi = cpuAbi2;
- }
- }
-
- // for debuggable packages, also extract gdbserver from lib/<abi>
- // into /data/data/<appname>/lib too.
- if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES &&
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- int result2 = cachePackageGdbServerLI(pkg, scanFile, cpuAbi);
- if (result2 == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_NATIVE_DEBUGGABLE;
- }
- }
- } catch (ZipException e) {
- Slog.w(TAG, "Failed to extract data from package file", e);
- return PackageManager.INSTALL_FAILED_INVALID_APK;
- } catch (IOException e) {
- Slog.w(TAG, "Failed to cache package shared libs", e);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ private File getNativeBinaryDirForPackage(PackageParser.Package pkg) {
+ final String nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
+ if (nativeLibraryDir != null) {
+ return new File(nativeLibraryDir);
+ } else {
+ // Fall back for old packages
+ return new File(pkg.applicationInfo.dataDir, LIB_DIR_NAME);
}
- return PackageManager.INSTALL_SUCCEEDED;
}
- private void cacheNativeBinaryLI(PackageParser.Package pkg,
- ZipFile zipFile, ZipEntry entry,
- File binaryDir,
- File binaryFile) throws IOException {
- InputStream inputStream = zipFile.getInputStream(entry);
- try {
- File tempFile = File.createTempFile("tmp", "tmp", binaryDir);
- String tempFilePath = tempFile.getPath();
- // XXX package manager can't change owner, so the executable files for
- // now need to be left as world readable and owned by the system.
- if (! FileUtils.copyToFile(inputStream, tempFile) ||
- ! tempFile.setLastModified(entry.getTime()) ||
- FileUtils.setPermissions(tempFilePath,
- FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
- |FileUtils.S_IXUSR|FileUtils.S_IXGRP|FileUtils.S_IXOTH
- |FileUtils.S_IROTH, -1, -1) != 0 ||
- ! tempFile.renameTo(binaryFile)) {
- // Failed to properly write file.
- tempFile.delete();
- throw new IOException("Couldn't create cached binary "
- + binaryFile + " in " + binaryDir);
- }
- } finally {
- inputStream.close();
- }
+ // Convenience call for removeNativeBinariesLI(File)
+ private void removeNativeBinariesLI(PackageParser.Package pkg) {
+ File nativeLibraryDir = getNativeBinaryDirForPackage(pkg);
+ removeNativeBinariesLI(nativeLibraryDir);
}
// Remove the native binaries of a given package. This simply
// gets rid of the files in the 'lib' sub-directory.
- private void removeNativeBinariesLI(PackageParser.Package pkg) {
- File binaryDir = getNativeBinaryDirForPackage(pkg);
-
+ public void removeNativeBinariesLI(File binaryDir) {
if (DEBUG_NATIVE) {
- Slog.w(TAG,"Deleting native binaries from: " + binaryDir.getPath());
+ Slog.w(TAG, "Deleting native binaries from: " + binaryDir.getPath());
}
// Just remove any file in the directory. Since the directory
@@ -3790,15 +3593,14 @@ class PackageManagerService extends IPackageManager.Stub {
// to have written anything there.
//
if (binaryDir.exists()) {
- File[] binaries = binaryDir.listFiles();
+ File[] binaries = binaryDir.listFiles();
if (binaries != null) {
- for (int nn=0; nn < binaries.length; nn++) {
+ for (int nn = 0; nn < binaries.length; nn++) {
if (DEBUG_NATIVE) {
- Slog.d(TAG," Deleting " + binaries[nn].getName());
+ Slog.d(TAG, " Deleting " + binaries[nn].getName());
}
if (!binaries[nn].delete()) {
- Slog.w(TAG,"Could not delete native binary: " +
- binaries[nn].getPath());
+ Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
}
}
}
@@ -4110,11 +3912,10 @@ class PackageManagerService extends IPackageManager.Stub {
|| (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (isSystemApp(pkg)) {
// For updated system applications, the signatureOrSystem permission
// is granted only if it had been defined by the original application.
- if ((pkg.applicationInfo.flags
- & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ if (isUpdatedSystemApp(pkg)) {
PackageSetting sysPs = mSettings.getDisabledSystemPkg(pkg.packageName);
if(sysPs.grantedPermissions.contains(perm)) {
allowed = true;
@@ -4552,6 +4353,8 @@ class PackageManagerService extends IPackageManager.Stub {
}
};
+ private static final boolean DEBUG_OBB = false;
+
private static final void sendPackageBroadcast(String action, String pkg,
Bundle extras, IIntentReceiver finishedReceiver) {
IActivityManager am = ActivityManagerNative.getDefault();
@@ -4726,6 +4529,29 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
+ public void setPackageObbPath(String packageName, String path) {
+ if (DEBUG_OBB)
+ Log.v(TAG, "Setting .obb path for " + packageName + " to: " + path);
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ final int permission = mContext.checkCallingPermission(
+ android.Manifest.permission.INSTALL_PACKAGES);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (!allowedByPermission && (uid != pkgSetting.userId)) {
+ throw new SecurityException("Permission denial: attempt to set .obb file from pid="
+ + Binder.getCallingPid() + ", uid=" + uid + ", package uid="
+ + pkgSetting.userId);
+ }
+ pkgSetting.obbPathString = path;
+ mSettings.writeLP();
+ }
+ }
+
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
@@ -4877,7 +4703,7 @@ class PackageManagerService extends IPackageManager.Stub {
// App explictly prefers external. Let policy decide
} else {
// Prefer previous location
- if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ if (isExternal(pkg)) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
@@ -4960,7 +4786,12 @@ class PackageManagerService extends IPackageManager.Stub {
@Override
void handleReturnCode() {
- processPendingInstall(mArgs, mRet);
+ // If mArgs is null, then MCS couldn't be reached. When it
+ // reconnects, it will try again to install. At that point, this
+ // will succeed.
+ if (mArgs != null) {
+ processPendingInstall(mArgs, mRet);
+ }
}
@Override
@@ -4984,16 +4815,16 @@ class PackageManagerService extends IPackageManager.Stub {
final InstallArgs srcArgs;
final InstallArgs targetArgs;
int mRet;
- MoveParams(InstallArgs srcArgs,
- IPackageMoveObserver observer,
- int flags, String packageName) {
+
+ MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
+ String packageName, String dataDir) {
this.srcArgs = srcArgs;
this.observer = observer;
this.flags = flags;
this.packageName = packageName;
if (srcArgs != null) {
Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath()));
- targetArgs = createInstallArgs(packageUri, flags, packageName);
+ targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir);
} else {
targetArgs = null;
}
@@ -5049,21 +4880,22 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath) {
+ private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath,
+ String nativeLibraryPath) {
if (installOnSd(flags)) {
- return new SdInstallArgs(fullCodePath, fullResourcePath);
+ return new SdInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath);
} else {
- return new FileInstallArgs(fullCodePath, fullResourcePath);
+ return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath);
}
}
- private InstallArgs createInstallArgs(Uri packageURI, int flags,
- String pkgName) {
+ // Used by package mover
+ private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir) {
if (installOnSd(flags)) {
String cid = getNextCodePath(null, pkgName, "/" + SdInstallArgs.RES_FILE_NAME);
return new SdInstallArgs(packageURI, cid);
} else {
- return new FileInstallArgs(packageURI, pkgName);
+ return new FileInstallArgs(packageURI, pkgName, dataDir);
}
}
@@ -5090,6 +4922,7 @@ class PackageManagerService extends IPackageManager.Stub {
abstract int doPostInstall(int status);
abstract String getCodePath();
abstract String getResourcePath();
+ abstract String getNativeLibraryPath();
// Need installer lock especially for dex file removal.
abstract void cleanUpResourcesLI();
abstract boolean doPostDeleteLI(boolean delete);
@@ -5100,6 +4933,7 @@ class PackageManagerService extends IPackageManager.Stub {
File installDir;
String codeFileName;
String resourceFileName;
+ String libraryPath;
boolean created = false;
FileInstallArgs(InstallParams params) {
@@ -5107,21 +4941,22 @@ class PackageManagerService extends IPackageManager.Stub {
params.flags, params.installerPackageName);
}
- FileInstallArgs(String fullCodePath, String fullResourcePath) {
+ FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
super(null, null, 0, null);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
resourceFileName = fullResourcePath;
+ libraryPath = nativeLibraryPath;
}
- FileInstallArgs(Uri packageURI, String pkgName) {
+ FileInstallArgs(Uri packageURI, String pkgName, String dataDir) {
super(packageURI, null, 0, null);
- boolean fwdLocked = isFwdLocked(flags);
- installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
+ installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
resourceFileName = getResourcePathFromCodePath();
+ libraryPath = new File(dataDir, LIB_DIR_NAME).getPath();
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
@@ -5133,8 +4968,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
void createCopyFile() {
- boolean fwdLocked = isFwdLocked(flags);
- installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
+ installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
codeFileName = createTempPackageFile(installDir).getPath();
resourceFileName = getResourcePathFromCodePath();
created = true;
@@ -5162,8 +4996,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
ParcelFileDescriptor out = null;
try {
- out = ParcelFileDescriptor.open(codeFile,
- ParcelFileDescriptor.MODE_READ_WRITE);
+ out = ParcelFileDescriptor.open(codeFile, ParcelFileDescriptor.MODE_READ_WRITE);
} catch (FileNotFoundException e) {
Slog.e(TAG, "Failed to create file descritpor for : " + codeFileName);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -5177,6 +5010,11 @@ class PackageManagerService extends IPackageManager.Stub {
} finally {
try { if (out != null) out.close(); } catch (IOException e) {}
}
+
+ if (!temp) {
+ NativeLibraryHelper.copyNativeBinariesLI(codeFile, new File(libraryPath));
+ }
+
return ret;
}
@@ -5232,6 +5070,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ @Override
+ String getNativeLibraryPath() {
+ return libraryPath;
+ }
+
private boolean cleanUp() {
boolean ret = true;
String sourceDir = getCodePath();
@@ -5268,11 +5111,14 @@ class PackageManagerService extends IPackageManager.Stub {
// we don't consider this to be a failure of the core package deletion
}
}
+ if (libraryPath != null) {
+ removeNativeBinariesLI(new File(libraryPath));
+ }
}
private boolean setPermissions() {
// TODO Do this in a more elegant way later on. for now just a hack
- if (!isFwdLocked(flags)) {
+ if (!isFwdLocked()) {
final int filePermissions =
FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
|FileUtils.S_IROTH;
@@ -5290,35 +5136,42 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean doPostDeleteLI(boolean delete) {
+ // XXX err, shouldn't we respect the delete flag?
cleanUpResourcesLI();
return true;
}
+
+ private boolean isFwdLocked() {
+ return (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
+ }
}
class SdInstallArgs extends InstallArgs {
- String cid;
- String cachePath;
static final String RES_FILE_NAME = "pkg.apk";
+ String cid;
+ String packagePath;
+ String libraryPath;
+
SdInstallArgs(InstallParams params) {
super(params.packageURI, params.observer,
params.flags, params.installerPackageName);
}
- SdInstallArgs(String fullCodePath, String fullResourcePath) {
+ SdInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
super(null, null, PackageManager.INSTALL_EXTERNAL, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
int sidx = subStr1.lastIndexOf("/");
cid = subStr1.substring(sidx+1, eidx);
- cachePath = subStr1;
+ setCachePath(subStr1);
}
SdInstallArgs(String cid) {
super(null, null, PackageManager.INSTALL_EXTERNAL, null);
this.cid = cid;
- cachePath = PackageHelper.getSdDir(cid);
+ setCachePath(PackageHelper.getSdDir(cid));
}
SdInstallArgs(Uri packageURI, String cid) {
@@ -5338,21 +5191,30 @@ class PackageManagerService extends IPackageManager.Stub {
if (temp) {
createCopyFile();
}
- cachePath = imcs.copyResourceToContainer(
+ String newCachePath = imcs.copyResourceToContainer(
packageURI, cid,
getEncryptKey(), RES_FILE_NAME);
- return (cachePath == null) ? PackageManager.INSTALL_FAILED_CONTAINER_ERROR :
- PackageManager.INSTALL_SUCCEEDED;
+ if (newCachePath != null) {
+ setCachePath(newCachePath);
+ return PackageManager.INSTALL_SUCCEEDED;
+ } else {
+ return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+ }
}
@Override
String getCodePath() {
- return cachePath + "/" + RES_FILE_NAME;
+ return packagePath;
}
@Override
String getResourcePath() {
- return cachePath + "/" + RES_FILE_NAME;
+ return packagePath;
+ }
+
+ @Override
+ String getNativeLibraryPath() {
+ return libraryPath;
}
int doPreInstall(int status) {
@@ -5362,8 +5224,11 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
boolean mounted = PackageHelper.isContainerMounted(cid);
if (!mounted) {
- cachePath = PackageHelper.mountSdDir(cid, getEncryptKey(), Process.SYSTEM_UID);
- if (cachePath == null) {
+ String newCachePath = PackageHelper.mountSdDir(cid, getEncryptKey(),
+ Process.SYSTEM_UID);
+ if (newCachePath != null) {
+ setCachePath(newCachePath);
+ } else {
return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
}
}
@@ -5409,13 +5274,19 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
Log.i(TAG, "Succesfully renamed " + cid +
- " at path: " + cachePath + " to " + newCacheId +
+ " to " + newCacheId +
" at new path: " + newCachePath);
cid = newCacheId;
- cachePath = newCachePath;
+ setCachePath(newCachePath);
return true;
}
+ private void setCachePath(String newCachePath) {
+ File cachePath = new File(newCachePath);
+ libraryPath = new File(cachePath, LIB_DIR_NAME).getPath();
+ packagePath = new File(cachePath, RES_FILE_NAME).getPath();
+ }
+
int doPostInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
@@ -5631,7 +5502,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
}
- boolean sysPkg = ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+ boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
} else {
@@ -5791,7 +5662,7 @@ class PackageManagerService extends IPackageManager.Stub {
!ps.codePathString.equals(oldPkgSetting.codePathString)) {
int installFlags = 0;
res.removedInfo.args = createInstallArgs(0, oldPkgSetting.codePathString,
- oldPkgSetting.resourcePathString);
+ oldPkgSetting.resourcePathString, oldPkgSetting.nativeLibraryPathString);
}
}
}
@@ -5803,8 +5674,17 @@ class PackageManagerService extends IPackageManager.Stub {
if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
if (retCode != 0) {
- Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ if (mNoDexOpt) {
+ /*
+ * If we're in an engineering build, programs are lazily run
+ * through dexopt. If the .dex file doesn't exist yet, it
+ * will be created when the program is run next.
+ */
+ Slog.i(TAG, "dex file doesn't exist, skipping move: " + newPackage.mPath);
+ } else {
+ Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ }
}
}
return PackageManager.INSTALL_SUCCEEDED;
@@ -5933,6 +5813,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Set application objects path explicitly after the rename
setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
+ pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode,
installerPackageName, res);
@@ -5947,8 +5828,7 @@ class PackageManagerService extends IPackageManager.Stub {
int retCode = 0;
// TODO Gross hack but fix later. Ideally move this to be a post installation
// check after alloting uid.
- if ((newPackage.applicationInfo.flags
- & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
+ if (isForwardLocked(newPackage)) {
File destResourceFile = new File(newPackage.applicationInfo.publicSourceDir);
try {
extractPublicFiles(newPackage, destResourceFile);
@@ -5983,12 +5863,20 @@ class PackageManagerService extends IPackageManager.Stub {
return PackageManager.INSTALL_SUCCEEDED;
}
- private boolean isForwardLocked(PackageParser.Package pkg) {
- return ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+ private static boolean isForwardLocked(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0;
+ }
+
+ private static boolean isExternal(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+ }
+
+ private static boolean isSystemApp(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
- private boolean isExternal(PackageParser.Package pkg) {
- return ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
+ private static boolean isUpdatedSystemApp(PackageParser.Package pkg) {
+ return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
private void extractPublicFiles(PackageParser.Package newPackage,
@@ -6209,24 +6097,24 @@ class PackageManagerService extends IPackageManager.Stub {
deletedPs = mSettings.mPackages.get(packageName);
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
- int retCode = mInstaller.remove(packageName);
+ int retCode = mInstaller.remove(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
+ packageName + ", retcode=" + retCode);
// we don't consider this to be a failure of the core package deletion
}
} else {
- //for emulator
+ // for simulator
PackageParser.Package pkg = mPackages.get(packageName);
File dataDir = new File(pkg.applicationInfo.dataDir);
dataDir.delete();
}
+ schedulePackageCleaning(packageName);
}
synchronized (mPackages) {
if (deletedPs != null) {
- schedulePackageCleaning(packageName);
-
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (outInfo != null) {
outInfo.removedUid = mSettings.removePackageLP(packageName);
@@ -6301,7 +6189,7 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
// Reinstate the old system package
mSettings.enableSystemPackageLP(p.packageName);
- // Remove any native libraries.
+ // Remove any native libraries. XXX needed?
removeNativeBinariesLI(p);
}
// Install the system package
@@ -6337,12 +6225,10 @@ class PackageManagerService extends IPackageManager.Stub {
// Delete application code and resources
if (deleteCodeAndResources) {
// TODO can pick up from PackageSettings as well
- int installFlags = ((p.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE)!=0) ?
- PackageManager.INSTALL_EXTERNAL : 0;
- installFlags |= ((p.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK)!=0) ?
- PackageManager.INSTALL_FORWARD_LOCK : 0;
- outInfo.args = createInstallArgs(installFlags,
- applicationInfo.sourceDir, applicationInfo.publicSourceDir);
+ int installFlags = isExternal(p) ? PackageManager.INSTALL_EXTERNAL : 0;
+ installFlags |= isForwardLocked(p) ? PackageManager.INSTALL_FORWARD_LOCK : 0;
+ outInfo.args = createInstallArgs(installFlags, applicationInfo.sourceDir,
+ applicationInfo.publicSourceDir, applicationInfo.nativeLibraryDir);
}
return true;
}
@@ -6387,7 +6273,7 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
boolean ret = false;
- if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (isSystemApp(p)) {
Log.i(TAG, "Removing system package:"+p.packageName);
// When an updated system application is deleted we delete the existing resources as well and
// fall back to existing code in system partition
@@ -6451,6 +6337,7 @@ class PackageManagerService extends IPackageManager.Stub {
p = ps.pkg;
}
}
+ boolean useEncryptedFSDir = false;
if(!dataOnly) {
//need to check this only for fully installed applications
@@ -6463,9 +6350,10 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
+ useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
}
if (mInstaller != null) {
- int retCode = mInstaller.clearUserData(packageName);
+ int retCode = mInstaller.clearUserData(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -6516,8 +6404,9 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
- int retCode = mInstaller.deleteCacheFiles(packageName);
+ int retCode = mInstaller.deleteCacheFiles(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -6579,9 +6468,10 @@ class PackageManagerService extends IPackageManager.Stub {
}
publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null;
}
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
int res = mInstaller.getSizeInfo(packageName, p.mPath,
- publicSrcDir, pStats);
+ publicSrcDir, pStats, useEncryptedFSDir);
if (res < 0) {
return false;
} else {
@@ -6814,6 +6704,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
pkgSetting.enabled = newState;
+ pkgSetting.pkg.mSetEnabled = newState;
} else {
// We're dealing with a component level state change
switch (newState) {
@@ -7082,12 +6973,23 @@ class PackageManagerService extends IPackageManager.Stub {
pw.print(" pkg="); pw.println(ps.pkg);
pw.print(" codePath="); pw.println(ps.codePathString);
pw.print(" resourcePath="); pw.println(ps.resourcePathString);
+ pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString);
+ pw.print(" obbPath="); pw.println(ps.obbPathString);
if (ps.pkg != null) {
pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion);
+ if (ps.pkg.mOperationPending) {
+ pw.println(" mOperationPending=true");
+ }
pw.print(" supportsScreens=[");
boolean first = true;
if ((ps.pkg.applicationInfo.flags &
+ ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ if (!first) pw.print(", ");
+ first = false;
+ pw.print("small");
+ }
+ if ((ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
if (!first) pw.print(", ");
first = false;
@@ -7100,10 +7002,10 @@ class PackageManagerService extends IPackageManager.Stub {
pw.print("large");
}
if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
if (!first) pw.print(", ");
first = false;
- pw.print("small");
+ pw.print("xlarge");
}
if ((ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
@@ -7627,7 +7529,8 @@ class PackageManagerService extends IPackageManager.Stub {
this.pkgFlags = pkgFlags & (
ApplicationInfo.FLAG_SYSTEM |
ApplicationInfo.FLAG_FORWARD_LOCK |
- ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+ ApplicationInfo.FLAG_EXTERNAL_STORAGE |
+ ApplicationInfo.FLAG_NEVER_ENCRYPT);
}
}
@@ -7641,6 +7544,8 @@ class PackageManagerService extends IPackageManager.Stub {
String codePathString;
File resourcePath;
String resourcePathString;
+ String nativeLibraryPathString;
+ String obbPathString;
private long timeStamp;
private String timeStampString = "0";
int versionCode;
@@ -7679,7 +7584,7 @@ class PackageManagerService extends IPackageManager.Stub {
this.resourcePathString = resourcePath.toString();
this.versionCode = pVersionCode;
}
-
+
public void setInstallerPackageName(String packageName) {
installerPackageName = packageName;
}
@@ -7894,11 +7799,17 @@ class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
// TODO(oam): This secure dir creation needs to be moved somewhere else (later)
+ File systemSecureDir = new File(dataDir, "secure/system");
systemDir.mkdirs();
+ systemSecureDir.mkdirs();
FileUtils.setPermissions(systemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
+ FileUtils.setPermissions(systemSecureDir.toString(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG
+ |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+ -1, -1);
mSettingsFilename = new File(systemDir, "packages.xml");
mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
mPackageListFilename = new File(systemDir, "packages.list");
@@ -8185,8 +8096,9 @@ class PackageManagerService extends IPackageManager.Stub {
private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) {
p.pkg = pkg;
- String codePath = pkg.applicationInfo.sourceDir;
- String resourcePath = pkg.applicationInfo.publicSourceDir;
+ pkg.mSetEnabled = p.enabled;
+ final String codePath = pkg.applicationInfo.sourceDir;
+ final String resourcePath = pkg.applicationInfo.publicSourceDir;
// Update code path if needed
if (!codePath.equalsIgnoreCase(p.codePathString)) {
Slog.w(TAG, "Code path for pkg : " + p.pkg.packageName +
@@ -8201,6 +8113,12 @@ class PackageManagerService extends IPackageManager.Stub {
p.resourcePath = new File(resourcePath);
p.resourcePathString = resourcePath;
}
+ // Update the native library path if needed
+ final String nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir;
+ if (nativeLibraryPath != null
+ && !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) {
+ p.nativeLibraryPathString = nativeLibraryPath;
+ }
// Update version code if needed
if (pkg.mVersionCode != p.versionCode) {
p.versionCode = pkg.mVersionCode;
@@ -8498,7 +8416,7 @@ class PackageManagerService extends IPackageManager.Stub {
StringBuilder sb = new StringBuilder();
for (PackageSetting pkg : mPackages.values()) {
ApplicationInfo ai = pkg.pkg.applicationInfo;
- String dataPath = ai.dataDir;
+ String dataPath = ai.dataDir;
boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Avoid any application that has a space in its path
@@ -8571,6 +8489,9 @@ class PackageManagerService extends IPackageManager.Stub {
if (!pkg.resourcePathString.equals(pkg.codePathString)) {
serializer.attribute(null, "resourcePath", pkg.resourcePathString);
}
+ if (pkg.nativeLibraryPathString != null) {
+ serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
+ }
if (pkg.sharedUser == null) {
serializer.attribute(null, "userId",
Integer.toString(pkg.userId));
@@ -8610,6 +8531,9 @@ class PackageManagerService extends IPackageManager.Stub {
if (!pkg.resourcePathString.equals(pkg.codePathString)) {
serializer.attribute(null, "resourcePath", pkg.resourcePathString);
}
+ if (pkg.nativeLibraryPathString != null) {
+ serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
+ }
serializer.attribute(null, "flags",
Integer.toString(pkg.pkgFlags));
serializer.attribute(null, "ts", pkg.getTimeStampStr());
@@ -8635,6 +8559,9 @@ class PackageManagerService extends IPackageManager.Stub {
if (pkg.installerPackageName != null) {
serializer.attribute(null, "installer", pkg.installerPackageName);
}
+ if (pkg.obbPathString != null) {
+ serializer.attribute(null, "obbPath", pkg.obbPathString);
+ }
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
serializer.startTag(null, "perms");
@@ -9011,6 +8938,8 @@ class PackageManagerService extends IPackageManager.Stub {
String sharedIdStr = null;
String codePathStr = null;
String resourcePathStr = null;
+ String nativeLibraryPathStr = null;
+ String obbPathStr = null;
String systemStr = null;
String installerPackageName = null;
String uidError = null;
@@ -9028,6 +8957,8 @@ class PackageManagerService extends IPackageManager.Stub {
sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
codePathStr = parser.getAttributeValue(null, "codePath");
resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ obbPathStr = parser.getAttributeValue(null, "obbPath");
version = parser.getAttributeValue(null, "version");
if (version != null) {
try {
@@ -9125,6 +9056,8 @@ class PackageManagerService extends IPackageManager.Stub {
if (packageSetting != null) {
packageSetting.uidError = "true".equals(uidError);
packageSetting.installerPackageName = installerPackageName;
+ packageSetting.nativeLibraryPathString = nativeLibraryPathStr;
+ packageSetting.obbPathString = obbPathStr;
final String enabledStr = parser.getAttributeValue(null, "enabled");
if (enabledStr != null) {
if (enabledStr.equalsIgnoreCase("true")) {
@@ -9404,6 +9337,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean isEnabledLP(ComponentInfo componentInfo, int flags) {
+ if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
+ return true;
+ }
final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
if (Config.LOGV) {
Log.v(TAG, "isEnabledLock - packageName = " + componentInfo.packageName
@@ -9419,14 +9355,20 @@ class PackageManagerService extends IPackageManager.Stub {
Debug.waitForDebugger();
Log.i(TAG, "We will crash!");
}
+ return false;
+ }
+ if (packageSettings.enabled == COMPONENT_ENABLED_STATE_DISABLED
+ || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
+ && packageSettings.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
+ return false;
+ }
+ if (packageSettings.enabledComponents.contains(componentInfo.name)) {
+ return true;
+ }
+ if (packageSettings.disabledComponents.contains(componentInfo.name)) {
+ return false;
}
- return ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0)
- || ((componentInfo.enabled
- && ((packageSettings.enabled == COMPONENT_ENABLED_STATE_ENABLED)
- || (componentInfo.applicationInfo.enabled
- && packageSettings.enabled != COMPONENT_ENABLED_STATE_DISABLED))
- && !packageSettings.disabledComponents.contains(componentInfo.name))
- || packageSettings.enabledComponents.contains(componentInfo.name));
+ return componentInfo.enabled;
}
}
@@ -9455,48 +9397,28 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- static String getTempContainerId() {
- String prefix = "smdl2tmp";
- int tmpIdx = 1;
- String list[] = PackageHelper.getSecureContainerList();
- if (list != null) {
- int idx = 0;
- int idList[] = new int[MAX_CONTAINERS];
- boolean neverFound = true;
- for (String name : list) {
- // Ignore null entries
- if (name == null) {
- continue;
- }
- int sidx = name.indexOf(prefix);
- if (sidx == -1) {
- // Not a temp file. just ignore
- continue;
- }
- String subStr = name.substring(sidx + prefix.length());
- idList[idx] = -1;
- if (subStr != null) {
- try {
- int cid = Integer.parseInt(subStr);
- idList[idx++] = cid;
- neverFound = false;
- } catch (NumberFormatException e) {
- }
- }
- }
- if (!neverFound) {
- // Sort idList
- Arrays.sort(idList);
- for (int j = 1; j <= idList.length; j++) {
- if (idList[j-1] != j) {
- tmpIdx = j;
- break;
- }
- }
- }
- }
- return prefix + tmpIdx;
- }
+ /* package */ static String getTempContainerId() {
+ int tmpIdx = 1;
+ String list[] = PackageHelper.getSecureContainerList();
+ if (list != null) {
+ for (final String name : list) {
+ // Ignore null and non-temporary container entries
+ if (name == null || !name.startsWith(mTempContainerPrefix)) {
+ continue;
+ }
+
+ String subStr = name.substring(mTempContainerPrefix.length());
+ try {
+ int cid = Integer.parseInt(subStr);
+ if (cid >= tmpIdx) {
+ tmpIdx = cid + 1;
+ }
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ return mTempContainerPrefix + tmpIdx;
+ }
/*
* Update media status on PackageManager.
@@ -9711,10 +9633,15 @@ class PackageManagerService extends IPackageManager.Stub {
if (doGc) {
Runtime.getRuntime().gc();
}
- // List stale containers.
+ // List stale containers and destroy stale temporary containers.
if (removeCids != null) {
for (String cid : removeCids) {
- Log.w(TAG, "Container " + cid + " is stale");
+ if (cid.startsWith(mTempContainerPrefix)) {
+ Log.i(TAG, "Destroying stale temporary container " + cid);
+ PackageHelper.destroySdDir(cid);
+ } else {
+ Log.w(TAG, "Container " + cid + " is stale");
+ }
}
}
}
@@ -9796,14 +9723,15 @@ class PackageManagerService extends IPackageManager.Stub {
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
} else {
// Disable moving fwd locked apps and system packages
- if (pkg.applicationInfo != null &&
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (pkg.applicationInfo != null && isSystemApp(pkg)) {
Slog.w(TAG, "Cannot move system application");
returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
- } else if (pkg.applicationInfo != null &&
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
+ } else if (pkg.applicationInfo != null && isForwardLocked(pkg)) {
Slog.w(TAG, "Cannot move forward locked app.");
returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED;
+ } else if (pkg.mOperationPending) {
+ Slog.w(TAG, "Attempt to move package which has pending operations");
+ returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING;
} else {
// Find install location first
if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 &&
@@ -9813,23 +9741,26 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ?
PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
- currFlags = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
- PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
+ currFlags = isExternal(pkg) ? PackageManager.INSTALL_EXTERNAL
+ : PackageManager.INSTALL_INTERNAL;
if (newFlags == currFlags) {
Slog.w(TAG, "No move required. Trying to move to same location");
returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
}
}
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+ pkg.mOperationPending = true;
+ }
}
}
if (returnCode != PackageManager.MOVE_SUCCEEDED) {
- processPendingMove(new MoveParams(null, observer, 0, packageName), returnCode);
+ processPendingMove(new MoveParams(null, observer, 0, packageName, null), returnCode);
} else {
Message msg = mHandler.obtainMessage(INIT_COPY);
InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
- pkg.applicationInfo.publicSourceDir);
- MoveParams mp = new MoveParams(srcArgs, observer, newFlags,
- packageName);
+ pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir);
+ MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
+ pkg.applicationInfo.dataDir);
msg.obj = mp;
mHandler.sendMessage(msg);
}
@@ -9847,7 +9778,7 @@ class PackageManagerService extends IPackageManager.Stub {
ArrayList<String> pkgList = null;
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(mp.packageName);
- if (pkg == null ) {
+ if (pkg == null) {
Slog.w(TAG, " Package " + mp.packageName +
" doesn't exist. Aborting move");
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
@@ -9880,9 +9811,10 @@ class PackageManagerService extends IPackageManager.Stub {
" Aborting move and returning error");
returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
} else {
- String oldCodePath = pkg.mPath;
- String newCodePath = mp.targetArgs.getCodePath();
- String newResPath = mp.targetArgs.getResourcePath();
+ final String oldCodePath = pkg.mPath;
+ final String newCodePath = mp.targetArgs.getCodePath();
+ final String newResPath = mp.targetArgs.getResourcePath();
+ final String newNativePath = mp.targetArgs.getNativeLibraryPath();
pkg.mPath = newCodePath;
// Move dex files around
if (moveDexFilesLI(pkg)
@@ -9895,11 +9827,13 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.mScanPath = newCodePath;
pkg.applicationInfo.sourceDir = newCodePath;
pkg.applicationInfo.publicSourceDir = newResPath;
+ pkg.applicationInfo.nativeLibraryDir = newNativePath;
PackageSetting ps = (PackageSetting) pkg.mExtras;
ps.codePath = new File(pkg.applicationInfo.sourceDir);
ps.codePathString = ps.codePath.getPath();
ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir);
ps.resourcePathString = ps.resourcePath.getPath();
+ ps.nativeLibraryPathString = newNativePath;
// Set the application info flag correctly.
if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
@@ -9932,6 +9866,18 @@ class PackageManagerService extends IPackageManager.Stub {
mp.srcArgs.doPostDeleteLI(true);
}
}
+
+ // Allow more operations on this file if we didn't fail because
+ // an operation was already pending for this package.
+ if (returnCode != PackageManager.MOVE_FAILED_OPERATION_PENDING) {
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ if (pkg != null) {
+ pkg.mOperationPending = false;
+ }
+ }
+ }
+
IPackageMoveObserver observer = mp.observer;
if (observer != null) {
try {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 493a348..16f3f10 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -151,6 +151,7 @@ class PowerManagerService extends IPowerManager.Stub
static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;
private final int MY_UID;
+ private final int MY_PID;
private boolean mDoneBooting = false;
private boolean mBootCompleted = false;
@@ -247,6 +248,9 @@ class PowerManagerService extends IPowerManager.Stub
private static final boolean mSpew = false;
private static final boolean mDebugProximitySensor = (true || mSpew);
private static final boolean mDebugLightSensor = (false || mSpew);
+
+ private native void nativeInit();
+ private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
/*
static PrintStream mLog;
@@ -306,7 +310,7 @@ class PowerManagerService extends IPowerManager.Stub
long ident = Binder.clearCallingIdentity();
try {
PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
- MY_UID, mTag);
+ MY_UID, MY_PID, mTag);
mHeld = true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -431,11 +435,11 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- PowerManagerService()
- {
+ PowerManagerService() {
// Hack to get our uid... should have a func for this.
long token = Binder.clearCallingIdentity();
- MY_UID = Binder.getCallingUid();
+ MY_UID = Process.myUid();
+ MY_PID = Process.myPid();
Binder.restoreCallingIdentity(token);
// XXX remove this when the kernel doesn't timeout wake locks
@@ -481,6 +485,11 @@ class PowerManagerService extends IPowerManager.Stub
}
}
}
+
+ nativeInit();
+ synchronized (mLocks) {
+ updateNativePowerStateLocked();
+ }
}
void initInThread() {
@@ -565,13 +574,13 @@ class PowerManagerService extends IPowerManager.Stub
private class WakeLock implements IBinder.DeathRecipient
{
- WakeLock(int f, IBinder b, String t, int u) {
+ WakeLock(int f, IBinder b, String t, int u, int p) {
super();
flags = f;
binder = b;
tag = t;
uid = u == MY_UID ? Process.SYSTEM_UID : u;
- pid = Binder.getCallingPid();
+ pid = p;
if (u != MY_UID || (
!"KEEP_SCREEN_ON_FLAG".equals(tag)
&& !"KeyInputQueue".equals(tag))) {
@@ -623,21 +632,23 @@ class PowerManagerService extends IPowerManager.Stub
public void acquireWakeLock(int flags, IBinder lock, String tag) {
int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
}
long ident = Binder.clearCallingIdentity();
try {
synchronized (mLocks) {
- acquireWakeLockLocked(flags, lock, uid, tag);
+ acquireWakeLockLocked(flags, lock, uid, pid, tag);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) {
+ public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) {
int acquireUid = -1;
+ int acquirePid = -1;
String acquireName = null;
int acquireType = -1;
@@ -649,7 +660,7 @@ class PowerManagerService extends IPowerManager.Stub
WakeLock wl;
boolean newlock;
if (index < 0) {
- wl = new WakeLock(flags, lock, tag, uid);
+ wl = new WakeLock(flags, lock, tag, uid, pid);
switch (wl.flags & LOCK_MASK)
{
case PowerManager.FULL_WAKE_LOCK:
@@ -722,13 +733,14 @@ class PowerManagerService extends IPowerManager.Stub
}
if (newlock) {
acquireUid = wl.uid;
+ acquirePid = wl.pid;
acquireName = wl.tag;
acquireType = wl.monitorType;
}
if (acquireType >= 0) {
try {
- mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType);
+ mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType);
} catch (RemoteException e) {
// Ignore
}
@@ -748,6 +760,7 @@ class PowerManagerService extends IPowerManager.Stub
private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
int releaseUid;
+ int releasePid;
String releaseName;
int releaseType;
@@ -792,13 +805,14 @@ class PowerManagerService extends IPowerManager.Stub
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
releaseUid = wl.uid;
+ releasePid = wl.pid;
releaseName = wl.tag;
releaseType = wl.monitorType;
if (releaseType >= 0) {
long origId = Binder.clearCallingIdentity();
try {
- mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType);
+ mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType);
} catch (RemoteException e) {
// Ignore
} finally {
@@ -1022,36 +1036,76 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- private void setTimeoutLocked(long now, int nextState)
- {
+ private void setTimeoutLocked(long now, int nextState) {
+ setTimeoutLocked(now, -1, nextState);
+ }
+
+ // If they gave a timeoutOverride it is the number of seconds
+ // to screen-off. Figure out where in the countdown cycle we
+ // should jump to.
+ private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) {
+ long timeoutOverride = originalTimeoutOverride;
if (mBootCompleted) {
- mHandler.removeCallbacks(mTimeoutTask);
- mTimeoutTask.nextState = nextState;
- long when = now;
- switch (nextState)
- {
- case SCREEN_BRIGHT:
- when += mKeylightDelay;
- break;
- case SCREEN_DIM:
- if (mDimDelay >= 0) {
- when += mDimDelay;
- break;
- } else {
- Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ synchronized (mLocks) {
+ long when = 0;
+ if (timeoutOverride <= 0) {
+ switch (nextState)
+ {
+ case SCREEN_BRIGHT:
+ when = now + mKeylightDelay;
+ break;
+ case SCREEN_DIM:
+ if (mDimDelay >= 0) {
+ when = now + mDimDelay;
+ break;
+ } else {
+ Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ }
+ case SCREEN_OFF:
+ synchronized (mLocks) {
+ when = now + mScreenOffDelay;
+ }
+ break;
+ default:
+ when = now;
+ break;
}
- case SCREEN_OFF:
- synchronized (mLocks) {
- when += mScreenOffDelay;
+ } else {
+ override: {
+ if (timeoutOverride <= mScreenOffDelay) {
+ when = now + timeoutOverride;
+ nextState = SCREEN_OFF;
+ break override;
+ }
+ timeoutOverride -= mScreenOffDelay;
+
+ if (mDimDelay >= 0) {
+ if (timeoutOverride <= mDimDelay) {
+ when = now + timeoutOverride;
+ nextState = SCREEN_DIM;
+ break override;
+ }
+ timeoutOverride -= mDimDelay;
+ }
+
+ when = now + timeoutOverride;
+ nextState = SCREEN_BRIGHT;
}
- break;
- }
- if (mSpew) {
- Slog.d(TAG, "setTimeoutLocked now=" + now + " nextState=" + nextState
- + " when=" + when);
+ }
+ if (mSpew) {
+ Slog.d(TAG, "setTimeoutLocked now=" + now
+ + " timeoutOverride=" + timeoutOverride
+ + " nextState=" + nextState + " when=" + when);
+ }
+
+ mHandler.removeCallbacks(mTimeoutTask);
+ mTimeoutTask.nextState = nextState;
+ mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0
+ ? (originalTimeoutOverride - timeoutOverride)
+ : -1;
+ mHandler.postAtTime(mTimeoutTask, when);
+ mNextTimeout = when; // for debugging
}
- mHandler.postAtTime(mTimeoutTask, when);
- mNextTimeout = when; // for debugging
}
}
@@ -1064,6 +1118,7 @@ class PowerManagerService extends IPowerManager.Stub
private class TimeoutTask implements Runnable
{
int nextState; // access should be synchronized on mLocks
+ long remainingTimeoutOverride;
public void run()
{
synchronized (mLocks) {
@@ -1084,11 +1139,11 @@ class PowerManagerService extends IPowerManager.Stub
{
case SCREEN_BRIGHT:
if (mDimDelay >= 0) {
- setTimeoutLocked(now, SCREEN_DIM);
+ setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM);
break;
}
case SCREEN_DIM:
- setTimeoutLocked(now, SCREEN_OFF);
+ setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF);
break;
}
}
@@ -1557,8 +1612,16 @@ class PowerManagerService extends IPowerManager.Stub
}
}
}
+
+ updateNativePowerStateLocked();
}
}
+
+ private void updateNativePowerStateLocked() {
+ nativeSetPowerState(
+ (mPowerState & SCREEN_ON_BIT) != 0,
+ (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
+ }
private int screenOffFinishedAnimatingLocked(int reason) {
// I don't think we need to check the current state here because all of these
@@ -1958,18 +2021,33 @@ class PowerManagerService extends IPowerManager.Stub
public void userActivityWithForce(long time, boolean noChangeLights, boolean force) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- userActivity(time, noChangeLights, OTHER_EVENT, force);
+ userActivity(time, -1, noChangeLights, OTHER_EVENT, force);
}
public void userActivity(long time, boolean noChangeLights) {
- userActivity(time, noChangeLights, OTHER_EVENT, false);
+ userActivity(time, -1, noChangeLights, OTHER_EVENT, false);
}
public void userActivity(long time, boolean noChangeLights, int eventType) {
- userActivity(time, noChangeLights, eventType, false);
+ userActivity(time, -1, noChangeLights, eventType, false);
}
public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) {
+ userActivity(time, -1, noChangeLights, eventType, force);
+ }
+
+ /*
+ * Reset the user activity timeout to now + timeout. This overrides whatever else is going
+ * on with user activity. Don't use this function.
+ */
+ public void clearUserActivityTimeout(long now, long timeout) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now");
+ userActivity(now, timeout, false, OTHER_EVENT, false);
+ }
+
+ private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
+ int eventType, boolean force) {
//mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)
@@ -2004,6 +2082,7 @@ class PowerManagerService extends IPowerManager.Stub
+ " mUserState=0x" + Integer.toHexString(mUserState)
+ " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
+ " mProximitySensorActive=" + mProximitySensorActive
+ + " timeoutOverride=" + timeoutOverride
+ " force=" + force);
}
// ignore user activity if we are in the process of turning off the screen
@@ -2041,7 +2120,7 @@ class PowerManagerService extends IPowerManager.Stub
mWakeLockState = mLocks.reactivateScreenLocksLocked();
setPowerState(mUserState | mWakeLockState, noChangeLights,
WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- setTimeoutLocked(time, SCREEN_BRIGHT);
+ setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);
}
}
}
diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java
deleted file mode 100644
index 9f5718f..0000000
--- a/services/java/com/android/server/SensorService.java
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.Context;
-import android.hardware.ISensorService;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.util.Config;
-import android.util.Slog;
-import android.util.PrintWriterPrinter;
-import android.util.Printer;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-import com.android.internal.app.IBatteryStats;
-import com.android.server.am.BatteryStatsService;
-
-
-/**
- * Class that manages the device's sensors. It register clients and activate
- * the needed sensors. The sensor events themselves are not broadcasted from
- * this service, instead, a file descriptor is provided to each client they
- * can read events from.
- */
-
-class SensorService extends ISensorService.Stub {
- static final String TAG = SensorService.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
- private static final int SENSOR_DISABLE = -1;
- private int mCurrentDelay = 0;
-
- /**
- * Battery statistics to be updated when sensors are enabled and disabled.
- */
- final IBatteryStats mBatteryStats = BatteryStatsService.getService();
-
- private final class Listener implements IBinder.DeathRecipient {
- final IBinder mToken;
- final int mUid;
-
- int mSensors = 0;
- int mDelay = 0x7FFFFFFF;
-
- Listener(IBinder token, int uid) {
- mToken = token;
- mUid = uid;
- }
-
- void addSensor(int sensor, int delay) {
- mSensors |= (1<<sensor);
- if (delay < mDelay)
- mDelay = delay;
- }
-
- void removeSensor(int sensor) {
- mSensors &= ~(1<<sensor);
- }
-
- boolean hasSensor(int sensor) {
- return ((mSensors & (1<<sensor)) != 0);
- }
-
- public void binderDied() {
- if (localLOGV) Slog.d(TAG, "sensor listener died");
- synchronized(mListeners) {
- mListeners.remove(this);
- mToken.unlinkToDeath(this, 0);
- // go through the lists of sensors used by the listener that
- // died and deactivate them.
- for (int sensor=0 ; sensor<32 && mSensors!=0 ; sensor++) {
- if (hasSensor(sensor)) {
- removeSensor(sensor);
- deactivateIfUnusedLocked(sensor);
- try {
- mBatteryStats.noteStopSensor(mUid, sensor);
- } catch (RemoteException e) {
- // oops. not a big deal.
- }
- }
- }
- if (mListeners.size() == 0) {
- _sensors_control_wake();
- _sensors_control_close();
- } else {
- // TODO: we should recalculate the delay, since removing
- // a listener may increase the overall rate.
- }
- mListeners.notify();
- }
- }
- }
-
- @SuppressWarnings("unused")
- public SensorService(Context context) {
- if (localLOGV) Slog.d(TAG, "SensorService startup");
- _sensors_control_init();
- }
-
- public Bundle getDataChannel() throws RemoteException {
- // synchronize so we do not require sensor HAL to be thread-safe.
- synchronized(mListeners) {
- return _sensors_control_open();
- }
- }
-
- public boolean enableSensor(IBinder binder, String name, int sensor, int enable)
- throws RemoteException {
-
- if (localLOGV) Slog.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable);
-
- if (binder == null) {
- Slog.e(TAG, "listener is null (sensor=" + name + ", id=" + sensor + ")");
- return false;
- }
-
- if (enable < 0 && (enable != SENSOR_DISABLE)) {
- Slog.e(TAG, "invalid enable parameter (enable=" + enable +
- ", sensor=" + name + ", id=" + sensor + ")");
- return false;
- }
-
- boolean res;
- int uid = Binder.getCallingUid();
- synchronized(mListeners) {
- res = enableSensorInternalLocked(binder, uid, name, sensor, enable);
- if (res == true) {
- // Inform battery statistics service of status change
- long identity = Binder.clearCallingIdentity();
- if (enable == SENSOR_DISABLE) {
- mBatteryStats.noteStopSensor(uid, sensor);
- } else {
- mBatteryStats.noteStartSensor(uid, sensor);
- }
- Binder.restoreCallingIdentity(identity);
- }
- }
- return res;
- }
-
- private boolean enableSensorInternalLocked(IBinder binder, int uid,
- String name, int sensor, int enable) throws RemoteException {
-
- // check if we have this listener
- Listener l = null;
- for (Listener listener : mListeners) {
- if (binder == listener.mToken) {
- l = listener;
- break;
- }
- }
-
- if (enable != SENSOR_DISABLE) {
- // Activate the requested sensor
- if (_sensors_control_activate(sensor, true) == false) {
- Slog.w(TAG, "could not enable sensor " + sensor);
- return false;
- }
-
- if (l == null) {
- /*
- * we don't have a listener for this binder yet, so
- * create a new one and add it to the list.
- */
- l = new Listener(binder, uid);
- binder.linkToDeath(l, 0);
- mListeners.add(l);
- mListeners.notify();
- }
-
- // take note that this sensor is now used by this client
- l.addSensor(sensor, enable);
-
- } else {
-
- if (l == null) {
- /*
- * This client isn't in the list, this usually happens
- * when enabling the sensor failed, but the client
- * didn't handle the error and later tries to shut that
- * sensor off.
- */
- Slog.w(TAG, "listener with binder " + binder +
- ", doesn't exist (sensor=" + name +
- ", id=" + sensor + ")");
- return false;
- }
-
- // remove this sensor from this client
- l.removeSensor(sensor);
-
- // see if we need to deactivate this sensors=
- deactivateIfUnusedLocked(sensor);
-
- // if the listener doesn't have any more sensors active
- // we can get rid of it
- if (l.mSensors == 0) {
- // we won't need this death notification anymore
- binder.unlinkToDeath(l, 0);
- // remove the listener from the list
- mListeners.remove(l);
- // and if the list is empty, turn off the whole sensor h/w
- if (mListeners.size() == 0) {
- _sensors_control_wake();
- _sensors_control_close();
- }
- mListeners.notify();
- }
- }
-
- // calculate and set the new delay
- int minDelay = 0x7FFFFFFF;
- for (Listener listener : mListeners) {
- if (listener.mDelay < minDelay)
- minDelay = listener.mDelay;
- }
- if (minDelay != 0x7FFFFFFF) {
- mCurrentDelay = minDelay;
- _sensors_control_set_delay(minDelay);
- }
-
- return true;
- }
-
- private void deactivateIfUnusedLocked(int sensor) {
- int size = mListeners.size();
- for (int i=0 ; i<size ; i++) {
- if (mListeners.get(i).hasSensor(sensor)) {
- // this sensor is still in use, don't turn it off
- return;
- }
- }
- if (_sensors_control_activate(sensor, false) == false) {
- Slog.w(TAG, "could not disable sensor " + sensor);
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- synchronized (mListeners) {
- Printer pr = new PrintWriterPrinter(pw);
- int c = 0;
- pr.println(mListeners.size() + " listener(s), delay=" + mCurrentDelay + " ms");
- for (Listener l : mListeners) {
- pr.println("listener[" + c + "] " +
- "sensors=0x" + Integer.toString(l.mSensors, 16) +
- ", uid=" + l.mUid +
- ", delay=" +
- l.mDelay + " ms");
- c++;
- }
- }
- }
-
- private ArrayList<Listener> mListeners = new ArrayList<Listener>();
-
- private static native int _sensors_control_init();
- private static native Bundle _sensors_control_open();
- private static native int _sensors_control_close();
- private static native boolean _sensors_control_activate(int sensor, boolean activate);
- private static native int _sensors_control_set_delay(int ms);
- private static native int _sensors_control_wake();
-}
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
new file mode 100644
index 0000000..4177432
--- /dev/null
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2007 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.PendingIntent;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A note on locking: We rely on the fact that calls onto mBar are oneway or
+ * if they are local, that they just enqueue messages to not deadlock.
+ */
+public class StatusBarManagerService extends IStatusBarService.Stub
+{
+ static final String TAG = "StatusBarManagerService";
+ static final boolean SPEW = true;
+
+ final Context mContext;
+ Handler mHandler = new Handler();
+ NotificationCallbacks mNotificationCallbacks;
+ volatile IStatusBar mBar;
+ StatusBarIconList mIcons = new StatusBarIconList();
+ HashMap<IBinder,StatusBarNotification> mNotifications
+ = new HashMap<IBinder,StatusBarNotification>();
+
+ // for disabling the status bar
+ ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
+ int mDisabled = 0;
+
+ private class DisableRecord implements IBinder.DeathRecipient {
+ String pkg;
+ int what;
+ IBinder token;
+
+ public void binderDied() {
+ Slog.i(TAG, "binder died for pkg=" + pkg);
+ disable(0, token, pkg);
+ token.unlinkToDeath(this, 0);
+ }
+ }
+
+ public interface NotificationCallbacks {
+ void onSetDisabled(int status);
+ void onClearAll();
+ void onNotificationClick(String pkg, String tag, int id);
+ void onPanelRevealed();
+ void onNotificationError(String pkg, String tag, int id,
+ int uid, int initialPid, String message);
+ }
+
+ /**
+ * Construct the service, add the status bar view to the window manager
+ */
+ public StatusBarManagerService(Context context) {
+ mContext = context;
+
+ final Resources res = context.getResources();
+ mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));
+ }
+
+ public void setNotificationCallbacks(NotificationCallbacks listener) {
+ mNotificationCallbacks = listener;
+ }
+
+ // ================================================================================
+ // Constructing the view
+ // ================================================================================
+
+ public void systemReady() {
+ }
+
+ public void systemReady2() {
+ ComponentName cn = ComponentName.unflattenFromString(
+ mContext.getString(com.android.internal.R.string.config_statusBarComponent));
+ Intent intent = new Intent();
+ intent.setComponent(cn);
+ Slog.i(TAG, "Starting service: " + cn);
+ mContext.startService(intent);
+ }
+
+ // ================================================================================
+ // From IStatusBarService
+ // ================================================================================
+ public void expand() {
+ enforceExpandStatusBar();
+
+ if (mBar != null) {
+ try {
+ mBar.animateExpand();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ public void collapse() {
+ enforceExpandStatusBar();
+
+ if (mBar != null) {
+ try {
+ mBar.animateCollapse();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ public void disable(int what, IBinder token, String pkg) {
+ enforceStatusBar();
+
+ // It's important that the the callback and the call to mBar get done
+ // in the same order when multiple threads are calling this function
+ // so they are paired correctly. The messages on the handler will be
+ // handled in the order they were enqueued, but will be outside the lock.
+ synchronized (mDisableRecords) {
+ manageDisableListLocked(what, token, pkg);
+ final int net = gatherDisableActionsLocked();
+ Slog.d(TAG, "disable... net=0x" + Integer.toHexString(net));
+ if (net != mDisabled) {
+ mDisabled = net;
+ mHandler.post(new Runnable() {
+ public void run() {
+ mNotificationCallbacks.onSetDisabled(net);
+ }
+ });
+ if (mBar != null) {
+ try {
+ mBar.disable(net);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+ }
+
+ public void setIcon(String slot, String iconPackage, int iconId, int iconLevel) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ StatusBarIcon icon = new StatusBarIcon(iconPackage, iconId, iconLevel);
+ //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
+ mIcons.setIcon(index, icon);
+
+ if (mBar != null) {
+ try {
+ mBar.setIcon(index, icon);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ public void setIconVisibility(String slot, boolean visible) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ StatusBarIcon icon = mIcons.getIcon(index);
+ if (icon == null) {
+ return;
+ }
+
+ if (icon.visible != visible) {
+ icon.visible = visible;
+
+ if (mBar != null) {
+ try {
+ mBar.setIcon(index, icon);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+ }
+
+ public void removeIcon(String slot) {
+ enforceStatusBar();
+
+ synchronized (mIcons) {
+ int index = mIcons.getSlotIndex(slot);
+ if (index < 0) {
+ throw new SecurityException("invalid status bar icon slot: " + slot);
+ }
+
+ mIcons.removeIcon(index);
+
+ if (mBar != null) {
+ try {
+ mBar.removeIcon(index);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ private void enforceStatusBar() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
+ "StatusBarManagerService");
+ }
+
+ private void enforceExpandStatusBar() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
+ "StatusBarManagerService");
+ }
+
+ private void enforceStatusBarService() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ "StatusBarManagerService");
+ }
+
+
+ // ================================================================================
+ // Callbacks from the status bar service.
+ // ================================================================================
+ public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
+ List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {
+ enforceStatusBarService();
+
+ Slog.i(TAG, "registerStatusBar bar=" + bar);
+ mBar = bar;
+ synchronized (mIcons) {
+ iconList.copyFrom(mIcons);
+ }
+ synchronized (mNotifications) {
+ for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
+ notificationKeys.add(e.getKey());
+ notifications.add(e.getValue());
+ }
+ }
+ }
+
+ /**
+ * The status bar service should call this each time the user brings the panel from
+ * invisible to visible in order to clear the notification light.
+ */
+ public void onPanelRevealed() {
+ enforceStatusBarService();
+
+ // tell the notification manager to turn off the lights.
+ mNotificationCallbacks.onPanelRevealed();
+ }
+
+ public void onNotificationClick(String pkg, String tag, int id) {
+ enforceStatusBarService();
+
+ mNotificationCallbacks.onNotificationClick(pkg, tag, id);
+ }
+
+ public void onNotificationError(String pkg, String tag, int id,
+ int uid, int initialPid, String message) {
+ enforceStatusBarService();
+
+ // WARNING: this will call back into us to do the remove. Don't hold any locks.
+ mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message);
+ }
+
+ public void onClearAllNotifications() {
+ enforceStatusBarService();
+
+ mNotificationCallbacks.onClearAll();
+ }
+
+ // ================================================================================
+ // Callbacks for NotificationManagerService.
+ // ================================================================================
+ public IBinder addNotification(StatusBarNotification notification) {
+ synchronized (mNotifications) {
+ IBinder key = new Binder();
+ mNotifications.put(key, notification);
+ if (mBar != null) {
+ try {
+ mBar.addNotification(key, notification);
+ } catch (RemoteException ex) {
+ }
+ }
+ return key;
+ }
+ }
+
+ public void updateNotification(IBinder key, StatusBarNotification notification) {
+ synchronized (mNotifications) {
+ if (!mNotifications.containsKey(key)) {
+ throw new IllegalArgumentException("updateNotification key not found: " + key);
+ }
+ mNotifications.put(key, notification);
+ if (mBar != null) {
+ try {
+ mBar.updateNotification(key, notification);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ public void removeNotification(IBinder key) {
+ synchronized (mNotifications) {
+ final StatusBarNotification n = mNotifications.remove(key);
+ if (n == null) {
+ throw new IllegalArgumentException("removeNotification key not found: " + key);
+ }
+ if (mBar != null) {
+ try {
+ mBar.removeNotification(key);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ // ================================================================================
+ // Can be called from any thread
+ // ================================================================================
+
+ // lock on mDisableRecords
+ void manageDisableListLocked(int what, IBinder token, String pkg) {
+ if (SPEW) {
+ Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
+ }
+ // update the list
+ synchronized (mDisableRecords) {
+ final int N = mDisableRecords.size();
+ DisableRecord tok = null;
+ int i;
+ for (i=0; i<N; i++) {
+ DisableRecord t = mDisableRecords.get(i);
+ if (t.token == token) {
+ tok = t;
+ break;
+ }
+ }
+ if (what == 0 || !token.isBinderAlive()) {
+ if (tok != null) {
+ mDisableRecords.remove(i);
+ tok.token.unlinkToDeath(tok, 0);
+ }
+ } else {
+ if (tok == null) {
+ tok = new DisableRecord();
+ try {
+ token.linkToDeath(tok, 0);
+ }
+ catch (RemoteException ex) {
+ return; // give up
+ }
+ mDisableRecords.add(tok);
+ }
+ tok.what = what;
+ tok.token = token;
+ tok.pkg = pkg;
+ }
+ }
+ }
+
+ // lock on mDisableRecords
+ int gatherDisableActionsLocked() {
+ final int N = mDisableRecords.size();
+ // gather the new net flags
+ int net = 0;
+ for (int i=0; i<N; i++) {
+ net |= mDisableRecords.get(i).what;
+ }
+ return net;
+ }
+
+ // ================================================================================
+ // Always called from UI thread
+ // ================================================================================
+
+ protected 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 StatusBar from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mIcons) {
+ mIcons.dump(pw);
+ }
+
+ synchronized (mNotifications) {
+ int i=0;
+ pw.println("Notification list:");
+ for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
+ pw.printf(" %2d: %s\n", i, e.getValue().toString());
+ i++;
+ }
+ }
+
+ synchronized (mDisableRecords) {
+ final int N = mDisableRecords.size();
+ pw.println(" mDisableRecords.size=" + N
+ + " mDisabled=0x" + Integer.toHexString(mDisabled));
+ for (int i=0; i<N; i++) {
+ DisableRecord tok = mDisableRecords.get(i);
+ pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what)
+ + " pkg=" + tok.pkg + " token=" + tok.token);
+ }
+ }
+ }
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_SCREEN_OFF.equals(action)) {
+ collapse();
+ }
+ /*
+ else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
+ updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
+ intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
+ }
+ else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ updateResources();
+ }
+ */
+ }
+ };
+
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9d5d035..1a209e2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -17,7 +17,7 @@
package com.android.server;
import com.android.server.am.ActivityManagerService;
-import com.android.server.status.StatusBarService;
+import com.android.server.sip.SipService;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
@@ -81,7 +81,8 @@ class ServerThread extends Thread {
android.os.Process.THREAD_PRIORITY_FOREGROUND);
BinderInternal.disableBackgroundScheduling(true);
-
+ android.os.Process.setCanSelfBackground(false);
+
String factoryTestStr = SystemProperties.get("ro.factorytest");
int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF
: Integer.parseInt(factoryTestStr);
@@ -97,6 +98,7 @@ class ServerThread extends Thread {
BluetoothA2dpService bluetoothA2dp = null;
HeadsetObserver headset = null;
DockObserver dock = null;
+ UsbObserver usb = null;
UiModeManagerService uiMode = null;
RecognitionManagerService recognition = null;
ThrottleService throttle = null;
@@ -164,10 +166,6 @@ class ServerThread extends Thread {
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
- // Sensor Service is needed by Window Manager, so this goes first
- Slog.i(TAG, "Sensor Service");
- ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));
-
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
@@ -206,7 +204,7 @@ class ServerThread extends Thread {
}
DevicePolicyManagerService devicePolicy = null;
- StatusBarService statusBar = null;
+ StatusBarManagerService statusBar = null;
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
NotificationManagerService notification = null;
@@ -224,10 +222,10 @@ class ServerThread extends Thread {
try {
Slog.i(TAG, "Status Bar");
- statusBar = new StatusBarService(context);
+ statusBar = new StatusBarManagerService(context);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
- Slog.e(TAG, "Failure starting StatusBarService", e);
+ Slog.e(TAG, "Failure starting StatusBarManagerService", e);
}
try {
@@ -374,8 +372,16 @@ class ServerThread extends Thread {
}
try {
+ Slog.i(TAG, "USB Observer");
+ // Listen for USB changes
+ usb = new UsbObserver(context);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting UsbObserver", e);
+ }
+
+ try {
Slog.i(TAG, "UI Mode Manager Service");
- // Listen for dock station changes
+ // Listen for UI mode changes
uiMode = new UiModeManagerService(context);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting UiModeManagerService", e);
@@ -405,14 +411,15 @@ class ServerThread extends Thread {
}
try {
- com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
+ Slog.i(TAG, "DiskStats Service");
+ ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
- Slog.e(TAG, "Failure installing status bar icons", e);
+ Slog.e(TAG, "Failure starting DiskStats Service", e);
}
try {
- Slog.i(TAG, "DiskStats Service");
- ServiceManager.addService("diskstats", new DiskStatsService(context));
+ Slog.i(TAG, "Sip Service");
+ ServiceManager.addService("sip", new SipService(context));
} catch (Throwable e) {
Slog.e(TAG, "Failure starting DiskStats Service", e);
}
@@ -464,9 +471,11 @@ class ServerThread extends Thread {
}
// These are needed to propagate to the runnable below.
+ final StatusBarManagerService statusBarF = statusBar;
final BatteryService batteryF = battery;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
+ final UsbObserver usbF = usb;
final ThrottleService throttleF = throttle;
final UiModeManagerService uiModeF = uiMode;
final AppWidgetService appWidgetF = appWidget;
@@ -485,9 +494,11 @@ class ServerThread extends Thread {
public void run() {
Slog.i(TAG, "Making services ready");
+ if (statusBarF != null) statusBarF.systemReady2();
if (batteryF != null) batteryF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
+ if (usbF != null) usbF.systemReady();
if (uiModeF != null) uiModeF.systemReady();
if (recognitionF != null) recognitionF.systemReady();
Watchdog.getInstance().start();
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 664dfa5..7e23422 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -19,6 +19,7 @@ package com.android.server;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -348,7 +349,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String[] apnTypes, String interfaceName, int networkType) {
+ String reason, String apn, String[] apnTypes, String interfaceName, int networkType,
+ String gateway) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
@@ -372,7 +374,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- apnTypes, interfaceName);
+ apnTypes, interfaceName, gateway);
}
public void notifyDataConnectionFailed(String reason) {
@@ -535,7 +537,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private void broadcastDataConnectionStateChanged(int state,
boolean isDataConnectivityPossible,
- String reason, String apn, String[] apnTypes, String interfaceName) {
+ String reason, String apn, String[] apnTypes, String interfaceName, String gateway) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
@@ -558,6 +560,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
+ int gatewayAddr = 0;
+ if (gateway != null) {
+ gatewayAddr = NetworkUtils.v4StringToInt(gateway);
+ }
+ intent.putExtra(Phone.DATA_GATEWAY_KEY, gatewayAddr);
+
mContext.sendStickyBroadcast(intent);
}
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 019245f..431cc39 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -566,7 +566,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
}
- // Fear not: StatusBarService manages a list of requests to disable
+ // Fear not: StatusBarManagerService manages a list of requests to disable
// features of the status bar; these are ORed together to form the
// active disabled list. So if (for example) the device is locked and
// the status bar should be totally disabled, the calls below will
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
new file mode 100644
index 0000000..d08fe9b
--- /dev/null
+++ b/services/java/com/android/server/UsbObserver.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.Usb;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UEventObserver;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.ArrayList;
+
+/**
+ * <p>UsbObserver monitors for changes to USB state.
+ */
+class UsbObserver extends UEventObserver {
+ private static final String TAG = UsbObserver.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ private static final String USB_CONFIGURATION_MATCH = "DEVPATH=/devices/virtual/switch/usb_configuration";
+ private static final String USB_FUNCTIONS_MATCH = "DEVPATH=/devices/virtual/usb_composite/";
+ private static final String USB_CONFIGURATION_PATH = "/sys/class/switch/usb_configuration/state";
+ private static final String USB_COMPOSITE_CLASS_PATH = "/sys/class/usb_composite";
+
+ private static final int MSG_UPDATE = 0;
+
+ private int mUsbConfig = 0;
+ private int mPreviousUsbConfig = 0;
+
+ // lists of enabled and disabled USB functions
+ private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
+ private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
+
+ private boolean mSystemReady;
+
+ private final Context mContext;
+
+ private PowerManagerService mPowerManager;
+
+ public UsbObserver(Context context) {
+ mContext = context;
+ init(); // set initial status
+
+ startObserving(USB_CONFIGURATION_MATCH);
+ startObserving(USB_FUNCTIONS_MATCH);
+ }
+
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "USB UEVENT: " + event.toString());
+ }
+
+ synchronized (this) {
+ String switchState = event.get("SWITCH_STATE");
+ if (switchState != null) {
+ try {
+ int newConfig = Integer.parseInt(switchState);
+ if (newConfig != mUsbConfig) {
+ mPreviousUsbConfig = mUsbConfig;
+ mUsbConfig = newConfig;
+ // trigger an Intent broadcast
+ if (mSystemReady) {
+ update();
+ }
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Could not parse switch state from event " + event);
+ }
+ } else {
+ String function = event.get("FUNCTION");
+ String enabledStr = event.get("ENABLED");
+ if (function != null && enabledStr != null) {
+ // Note: we do not broadcast a change when a function is enabled or disabled.
+ // We just record the state change for the next broadcast.
+ boolean enabled = "1".equals(enabledStr);
+ if (enabled) {
+ if (!mEnabledFunctions.contains(function)) {
+ mEnabledFunctions.add(function);
+ }
+ mDisabledFunctions.remove(function);
+ } else {
+ if (!mDisabledFunctions.contains(function)) {
+ mDisabledFunctions.add(function);
+ }
+ mEnabledFunctions.remove(function);
+ }
+ }
+ }
+ }
+ }
+ private final void init() {
+ char[] buffer = new char[1024];
+
+ try {
+ FileReader file = new FileReader(USB_CONFIGURATION_PATH);
+ int len = file.read(buffer, 0, 1024);
+ mPreviousUsbConfig = mUsbConfig = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have USB configuration switch support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+
+ try {
+ File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
+ for (int i = 0; i < files.length; i++) {
+ File file = new File(files[i], "enable");
+ FileReader reader = new FileReader(file);
+ int len = reader.read(buffer, 0, 1024);
+ int value = Integer.valueOf((new String(buffer, 0, len)).trim());
+ String functionName = files[i].getName();
+ if (value == 1) {
+ mEnabledFunctions.add(functionName);
+ } else {
+ mDisabledFunctions.add(functionName);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have USB composite class support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+ }
+
+ void systemReady() {
+ synchronized (this) {
+ update();
+ mSystemReady = true;
+ }
+ }
+
+ private final void update() {
+ mHandler.sendEmptyMessage(MSG_UPDATE);
+ }
+
+ private final Handler mHandler = new Handler() {
+ private void addEnabledFunctions(Intent intent) {
+ // include state of all USB functions in our extras
+ for (int i = 0; i < mEnabledFunctions.size(); i++) {
+ intent.putExtra(mEnabledFunctions.get(i), Usb.USB_FUNCTION_ENABLED);
+ }
+ for (int i = 0; i < mDisabledFunctions.size(); i++) {
+ intent.putExtra(mDisabledFunctions.get(i), Usb.USB_FUNCTION_DISABLED);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE:
+ synchronized (this) {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ if (Settings.Secure.getInt(cr,
+ Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+ Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
+ return;
+ }
+ // Send an Intent containing connected/disconnected state
+ // and the enabled/disabled state of all USB functions
+ Intent intent;
+ boolean usbConnected = (mUsbConfig != 0);
+ if (usbConnected) {
+ intent = new Intent(Usb.ACTION_USB_CONNECTED);
+ addEnabledFunctions(intent);
+ } else {
+ intent = new Intent(Usb.ACTION_USB_DISCONNECTED);
+ }
+ mContext.sendBroadcast(intent);
+
+ // send a sticky broadcast for clients interested in both connect and disconnect
+ intent = new Intent(Usb.ACTION_USB_STATE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(Usb.USB_CONNECTED, usbConnected);
+ addEnabledFunctions(intent);
+ mContext.sendStickyBroadcast(intent);
+ }
+ break;
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/ViewServer.java b/services/java/com/android/server/ViewServer.java
index ae00438..7b5d18a 100644
--- a/services/java/com/android/server/ViewServer.java
+++ b/services/java/com/android/server/ViewServer.java
@@ -21,6 +21,8 @@ import android.util.Slog;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@@ -41,11 +43,13 @@ class ViewServer implements Runnable {
*/
public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
+ private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
+
// Debug facility
private static final String LOG_TAG = "ViewServer";
- private static final String VALUE_PROTOCOL_VERSION = "2";
- private static final String VALUE_SERVER_VERSION = "3";
+ private static final String VALUE_PROTOCOL_VERSION = "3";
+ private static final String VALUE_SERVER_VERSION = "4";
// Protocol commands
// Returns the protocol version
@@ -54,6 +58,10 @@ class ViewServer implements Runnable {
private static final String COMMAND_SERVER_VERSION = "SERVER";
// Lists all of the available windows in the system
private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
+ // Keeps a connection open and notifies when the list of windows changes
+ private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
+ // Returns the focused window
+ private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
private ServerSocket mServer;
private Thread mThread;
@@ -61,6 +69,8 @@ class ViewServer implements Runnable {
private final WindowManagerService mWindowManager;
private final int mPort;
+ private ExecutorService mThreadPool;
+
/**
* Creates a new ViewServer associated with the specified window manager.
* The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
@@ -103,8 +113,9 @@ class ViewServer implements Runnable {
return false;
}
- mServer = new ServerSocket(mPort, 1, InetAddress.getLocalHost());
+ mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
+ mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
mThread.start();
return true;
@@ -122,7 +133,16 @@ class ViewServer implements Runnable {
*/
boolean stop() {
if (mThread != null) {
+
mThread.interrupt();
+ if (mThreadPool != null) {
+ try {
+ mThreadPool.shutdownNow();
+ } catch (SecurityException e) {
+ Slog.w(LOG_TAG, "Could not stop all view server threads");
+ }
+ }
+ mThreadPool = null;
mThread = null;
try {
mServer.close();
@@ -152,62 +172,21 @@ class ViewServer implements Runnable {
* Main server loop.
*/
public void run() {
- final ServerSocket server = mServer;
-
while (Thread.currentThread() == mThread) {
- Socket client = null;
// Any uncaught exception will crash the system process
try {
- client = server.accept();
-
- BufferedReader in = null;
- try {
- in = new BufferedReader(new InputStreamReader(client.getInputStream()), 1024);
-
- final String request = in.readLine();
-
- String command;
- String parameters;
-
- int index = request.indexOf(' ');
- if (index == -1) {
- command = request;
- parameters = "";
- } else {
- command = request.substring(0, index);
- parameters = request.substring(index + 1);
- }
-
- boolean result;
- if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
- result = writeValue(client, VALUE_PROTOCOL_VERSION);
- } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
- result = writeValue(client, VALUE_SERVER_VERSION);
- } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
- result = mWindowManager.viewServerListWindows(client);
- } else {
- result = mWindowManager.viewServerWindowCommand(client,
- command, parameters);
- }
-
- if (!result) {
- Slog.w(LOG_TAG, "An error occured with the command: " + command);
- }
- } finally {
- if (in != null) {
- in.close();
- }
- }
- } catch (Exception e) {
- Slog.w(LOG_TAG, "Connection error: ", e);
- } finally {
- if (client != null) {
+ Socket client = mServer.accept();
+ if(mThreadPool != null) {
+ mThreadPool.submit(new ViewServerWorker(client));
+ } else {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
+ } catch (Exception e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
}
}
}
@@ -235,4 +214,133 @@ class ViewServer implements Runnable {
}
return result;
}
+
+ class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
+ private Socket mClient;
+ private boolean mNeedWindowListUpdate;
+ private boolean mNeedFocusedWindowUpdate;
+ public ViewServerWorker(Socket client) {
+ mClient = client;
+ mNeedWindowListUpdate = false;
+ mNeedFocusedWindowUpdate = false;
+ }
+
+ public void run() {
+
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
+
+ final String request = in.readLine();
+
+ String command;
+ String parameters;
+
+ int index = request.indexOf(' ');
+ if (index == -1) {
+ command = request;
+ parameters = "";
+ } else {
+ command = request.substring(0, index);
+ parameters = request.substring(index + 1);
+ }
+
+ boolean result;
+ if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
+ result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
+ } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
+ result = writeValue(mClient, VALUE_SERVER_VERSION);
+ } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
+ result = mWindowManager.viewServerListWindows(mClient);
+ } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
+ result = mWindowManager.viewServerGetFocusedWindow(mClient);
+ } else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
+ result = windowManagerAutolistLoop();
+ } else {
+ result = mWindowManager.viewServerWindowCommand(mClient,
+ command, parameters);
+ }
+
+ if (!result) {
+ Slog.w(LOG_TAG, "An error occured with the command: " + command);
+ }
+ } catch(IOException e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (mClient != null) {
+ try {
+ mClient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public void windowsChanged() {
+ synchronized(this) {
+ mNeedWindowListUpdate = true;
+ notifyAll();
+ }
+ }
+
+ public void focusChanged() {
+ synchronized(this) {
+ mNeedFocusedWindowUpdate = true;
+ notifyAll();
+ }
+ }
+
+ private boolean windowManagerAutolistLoop() {
+ mWindowManager.addWindowChangeListener(this);
+ BufferedWriter out = null;
+ try {
+ out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
+ while (!Thread.interrupted()) {
+ boolean needWindowListUpdate = false;
+ boolean needFocusedWindowUpdate = false;
+ synchronized (this) {
+ while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
+ wait();
+ }
+ if (mNeedWindowListUpdate) {
+ mNeedWindowListUpdate = false;
+ needWindowListUpdate = true;
+ }
+ if (mNeedFocusedWindowUpdate) {
+ mNeedFocusedWindowUpdate = false;
+ needFocusedWindowUpdate = true;
+ }
+ }
+ if(needWindowListUpdate) {
+ out.write("LIST UPDATE\n");
+ out.flush();
+ }
+ if(needFocusedWindowUpdate) {
+ out.write("FOCUS UPDATE\n");
+ out.flush();
+ }
+ }
+ } catch (Exception e) {
+ Slog.w(LOG_TAG, "Connection error: ", e);
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ }
+ }
+ mWindowManager.removeWindowChangeListener(this);
+ }
+ return true;
+ }
+ }
}
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index d4133f3..a742093 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -57,20 +57,10 @@ public class Watchdog extends Thread {
static final boolean RECORD_KERNEL_THREADS = true;
static final int MONITOR = 2718;
- static final int GLOBAL_PSS = 2719;
static final int TIME_TO_RESTART = DB ? 15*1000 : 60*1000;
static final int TIME_TO_WAIT = TIME_TO_RESTART / 2;
- static final int MEMCHECK_DEFAULT_INTERVAL = DB ? 30 : 30*60; // 30 minutes
- static final int MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL = DB ? 60 : 2*60*60; // 2 hours
- static final int MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD = (DB ? 10:16)*1024*1024; // 16MB
- static final int MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD = (DB ? 14:20)*1024*1024; // 20MB
- static final int MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD = (DB ? 4:8)*1024*1024; // 8MB
- static final int MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD = (DB ? 8:12)*1024*1024; // 12MB
-
- static final int MEMCHECK_DEFAULT_EXEC_START_TIME = 1*60*60; // 1:00am
- static final int MEMCHECK_DEFAULT_EXEC_END_TIME = 5*60*60; // 5:00am
static final int MEMCHECK_DEFAULT_MIN_SCREEN_OFF = DB ? 1*60 : 5*60; // 5 minutes
static final int MEMCHECK_DEFAULT_MIN_ALARM = DB ? 1*60 : 3*60; // 3 minutes
static final int MEMCHECK_DEFAULT_RECHECK_INTERVAL = DB ? 1*60 : 5*60; // 5 minutes
@@ -79,14 +69,12 @@ public class Watchdog extends Thread {
static final int REBOOT_DEFAULT_START_TIME = 3*60*60; // 3:00am
static final int REBOOT_DEFAULT_WINDOW = 60*60; // within 1 hour
- static final String CHECKUP_ACTION = "com.android.service.Watchdog.CHECKUP";
static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT";
static Watchdog sWatchdog;
/* This handler will be used to post message back onto the main thread */
final Handler mHandler;
- final Runnable mGlobalPssCollected;
final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
ContentResolver mResolver;
BatteryService mBattery;
@@ -97,31 +85,9 @@ public class Watchdog extends Thread {
boolean mForceKillSystem;
Monitor mCurrentMonitor;
- PssRequestor mPhoneReq;
int mPhonePid;
- int mPhonePss;
-
- long mLastMemCheckTime = -(MEMCHECK_DEFAULT_INTERVAL*1000);
- boolean mHavePss;
- long mLastMemCheckRealtime = -(MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL*1000);
- boolean mHaveGlobalPss;
- final MemMonitor mSystemMemMonitor = new MemMonitor("system",
- Settings.Secure.MEMCHECK_SYSTEM_ENABLED,
- Settings.Secure.MEMCHECK_SYSTEM_SOFT_THRESHOLD,
- MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD,
- Settings.Secure.MEMCHECK_SYSTEM_HARD_THRESHOLD,
- MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD);
- final MemMonitor mPhoneMemMonitor = new MemMonitor("com.android.phone",
- Settings.Secure.MEMCHECK_PHONE_ENABLED,
- Settings.Secure.MEMCHECK_PHONE_SOFT_THRESHOLD,
- MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD,
- Settings.Secure.MEMCHECK_PHONE_HARD_THRESHOLD,
- MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD);
final Calendar mCalendar = Calendar.getInstance();
- long mMemcheckLastTime;
- long mMemcheckExecStartTime;
- long mMemcheckExecEndTime;
int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF;
int mMinAlarm = MEMCHECK_DEFAULT_MIN_ALARM;
boolean mNeedScheduledCheck;
@@ -140,126 +106,13 @@ public class Watchdog extends Thread {
int mReqRecheckInterval= -1; // >= 0 if a specific recheck interval has been requested
/**
- * This class monitors the memory in a particular process.
- */
- final class MemMonitor {
- final String mProcessName;
- final String mEnabledSetting;
- final String mSoftSetting;
- final String mHardSetting;
-
- int mSoftThreshold;
- int mHardThreshold;
- boolean mEnabled;
- long mLastPss;
-
- static final int STATE_OK = 0;
- static final int STATE_SOFT = 1;
- static final int STATE_HARD = 2;
- int mState;
-
- MemMonitor(String processName, String enabledSetting,
- String softSetting, int defSoftThreshold,
- String hardSetting, int defHardThreshold) {
- mProcessName = processName;
- mEnabledSetting = enabledSetting;
- mSoftSetting = softSetting;
- mHardSetting = hardSetting;
- mSoftThreshold = defSoftThreshold;
- mHardThreshold = defHardThreshold;
- }
-
- void retrieveSettings(ContentResolver resolver) {
- mSoftThreshold = Settings.Secure.getInt(
- resolver, mSoftSetting, mSoftThreshold);
- mHardThreshold = Settings.Secure.getInt(
- resolver, mHardSetting, mHardThreshold);
- mEnabled = Settings.Secure.getInt(
- resolver, mEnabledSetting, 0) != 0;
- }
-
- boolean checkLocked(long curTime, int pid, int pss) {
- mLastPss = pss;
- if (mLastPss < mSoftThreshold) {
- mState = STATE_OK;
- } else if (mLastPss < mHardThreshold) {
- mState = STATE_SOFT;
- } else {
- mState = STATE_HARD;
- }
- EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_PSS, mProcessName, pid, mLastPss);
-
- if (mState == STATE_OK) {
- // Memory is good, don't recover.
- return false;
- }
-
- if (mState == STATE_HARD) {
- // Memory is really bad, kill right now.
- EventLog.writeEvent(EventLogTags.WATCHDOG_HARD_RESET, mProcessName, pid,
- mHardThreshold, mLastPss);
- return mEnabled;
- }
-
- // It is time to schedule a reset...
- // Check if we are currently within the time to kill processes due
- // to memory use.
- computeMemcheckTimesLocked(curTime);
- String skipReason = null;
- if (curTime < mMemcheckExecStartTime || curTime > mMemcheckExecEndTime) {
- skipReason = "time";
- } else {
- skipReason = shouldWeBeBrutalLocked(curTime);
- }
- EventLog.writeEvent(EventLogTags.WATCHDOG_SOFT_RESET, mProcessName, pid,
- mSoftThreshold, mLastPss, skipReason != null ? skipReason : "");
- if (skipReason != null) {
- mNeedScheduledCheck = true;
- return false;
- }
- return mEnabled;
- }
-
- void clear() {
- mLastPss = 0;
- mState = STATE_OK;
- }
- }
-
- /**
* Used for scheduling monitor callbacks and checking memory usage.
*/
final class HeartbeatHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case GLOBAL_PSS: {
- if (mHaveGlobalPss) {
- // During the last pass we collected pss information, so
- // now it is time to report it.
- mHaveGlobalPss = false;
- if (localLOGV) Slog.v(TAG, "Received global pss, logging.");
- logGlobalMemory();
- }
- } break;
-
case MONITOR: {
- if (mHavePss) {
- // During the last pass we collected pss information, so
- // now it is time to report it.
- mHavePss = false;
- if (localLOGV) Slog.v(TAG, "Have pss, checking memory.");
- checkMemory();
- }
-
- if (mHaveGlobalPss) {
- // During the last pass we collected pss information, so
- // now it is time to report it.
- mHaveGlobalPss = false;
- if (localLOGV) Slog.v(TAG, "Have global pss, logging.");
- logGlobalMemory();
- }
-
long now = SystemClock.uptimeMillis();
// See if we should force a reboot.
@@ -274,32 +127,6 @@ public class Watchdog extends Thread {
checkReboot(false);
}
- // See if we should check memory conditions.
- long memCheckInterval = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_INTERVAL,
- MEMCHECK_DEFAULT_INTERVAL) * 1000;
- if ((mLastMemCheckTime+memCheckInterval) < now) {
- // It is now time to collect pss information. This
- // is async so we won't report it now. And to keep
- // things simple, we will assume that everyone has
- // reported back by the next MONITOR message.
- mLastMemCheckTime = now;
- if (localLOGV) Slog.v(TAG, "Collecting memory usage.");
- collectMemory();
- mHavePss = true;
-
- long memCheckRealtimeInterval = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_LOG_REALTIME_INTERVAL,
- MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL) * 1000;
- long realtimeNow = SystemClock.elapsedRealtime();
- if ((mLastMemCheckRealtime+memCheckRealtimeInterval) < realtimeNow) {
- mLastMemCheckRealtime = realtimeNow;
- if (localLOGV) Slog.v(TAG, "Collecting global memory usage.");
- collectGlobalMemory();
- mHaveGlobalPss = true;
- }
- }
-
final int size = mMonitors.size();
for (int i = 0 ; i < size ; i++) {
mCurrentMonitor = mMonitors.get(i);
@@ -315,20 +142,6 @@ public class Watchdog extends Thread {
}
}
- final class GlobalPssCollected implements Runnable {
- public void run() {
- mHandler.sendEmptyMessage(GLOBAL_PSS);
- }
- }
-
- final class CheckupReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context c, Intent intent) {
- if (localLOGV) Slog.v(TAG, "Alarm went off, checking memory.");
- checkMemory();
- }
- }
-
final class RebootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context c, Intent intent) {
@@ -359,27 +172,6 @@ public class Watchdog extends Thread {
void monitor();
}
- public interface PssRequestor {
- void requestPss();
- }
-
- public class PssStats {
- public int mEmptyPss;
- public int mEmptyCount;
- public int mBackgroundPss;
- public int mBackgroundCount;
- public int mServicePss;
- public int mServiceCount;
- public int mVisiblePss;
- public int mVisibleCount;
- public int mForegroundPss;
- public int mForegroundCount;
-
- public int mNoPssCount;
-
- public int mProcDeaths[] = new int[10];
- }
-
public static Watchdog getInstance() {
if (sWatchdog == null) {
sWatchdog = new Watchdog();
@@ -391,7 +183,6 @@ public class Watchdog extends Thread {
private Watchdog() {
super("watchdog");
mHandler = new HeartbeatHandler();
- mGlobalPssCollected = new GlobalPssCollected();
}
public void init(Context context, BatteryService battery,
@@ -403,11 +194,6 @@ public class Watchdog extends Thread {
mAlarm = alarm;
mActivity = activity;
- context.registerReceiver(new CheckupReceiver(),
- new IntentFilter(CHECKUP_ACTION));
- mCheckupIntent = PendingIntent.getBroadcast(context,
- 0, new Intent(CHECKUP_ACTION), 0);
-
context.registerReceiver(new RebootReceiver(),
new IntentFilter(REBOOT_ACTION));
mRebootIntent = PendingIntent.getBroadcast(context,
@@ -420,20 +206,10 @@ public class Watchdog extends Thread {
mBootTime = System.currentTimeMillis();
}
- public void processStarted(PssRequestor req, String name, int pid) {
+ public void processStarted(String name, int pid) {
synchronized (this) {
if ("com.android.phone".equals(name)) {
- mPhoneReq = req;
mPhonePid = pid;
- mPhonePss = 0;
- }
- }
- }
-
- public void reportPss(PssRequestor req, String name, int pss) {
- synchronized (this) {
- if (mPhoneReq == req) {
- mPhonePss = pss;
}
}
}
@@ -447,152 +223,6 @@ public class Watchdog extends Thread {
}
}
- /**
- * Retrieve memory usage information from specific processes being
- * monitored. This is an async operation, so must be done before doing
- * memory checks.
- */
- void collectMemory() {
- synchronized (this) {
- if (mPhoneReq != null) {
- mPhoneReq.requestPss();
- }
- }
- }
-
- /**
- * Retrieve memory usage over all application processes. This is an
- * async operation, so must be done before doing memory checks.
- */
- void collectGlobalMemory() {
- mActivity.requestPss(mGlobalPssCollected);
- }
-
- /**
- * Check memory usage in the system, scheduling kills/reboots as needed.
- * This always runs on the mHandler thread.
- */
- void checkMemory() {
- boolean needScheduledCheck;
- long curTime;
- long nextTime = 0;
-
- long recheckInterval = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_RECHECK_INTERVAL,
- MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000;
-
- mSystemMemMonitor.retrieveSettings(mResolver);
- mPhoneMemMonitor.retrieveSettings(mResolver);
- retrieveBrutalityAmount();
-
- synchronized (this) {
- curTime = System.currentTimeMillis();
- mNeedScheduledCheck = false;
-
- // How is the system doing?
- if (mSystemMemMonitor.checkLocked(curTime, Process.myPid(),
- (int)Process.getPss(Process.myPid()))) {
- // Not good! Time to suicide.
- mForceKillSystem = true;
- notifyAll();
- return;
- }
-
- // How is the phone process doing?
- if (mPhoneReq != null) {
- if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
- mPhonePss)) {
- // Just kill the phone process and let it restart.
- Slog.i(TAG, "Watchdog is killing the phone process");
- Process.killProcess(mPhonePid);
- }
- } else {
- mPhoneMemMonitor.clear();
- }
-
- needScheduledCheck = mNeedScheduledCheck;
- if (needScheduledCheck) {
- // Something is going bad, but now is not a good time to
- // tear things down... schedule an alarm to check again soon.
- nextTime = curTime + recheckInterval;
- if (nextTime < mMemcheckExecStartTime) {
- nextTime = mMemcheckExecStartTime;
- } else if (nextTime >= mMemcheckExecEndTime){
- // Need to check during next exec time... so that needs
- // to be computed.
- if (localLOGV) Slog.v(TAG, "Computing next time range");
- computeMemcheckTimesLocked(nextTime);
- nextTime = mMemcheckExecStartTime;
- }
-
- if (localLOGV) {
- mCalendar.setTimeInMillis(nextTime);
- Slog.v(TAG, "Next Alarm Time: " + mCalendar);
- }
- }
- }
-
- if (needScheduledCheck) {
- if (localLOGV) Slog.v(TAG, "Scheduling next memcheck alarm for "
- + ((nextTime-curTime)/1000/60) + "m from now");
- mAlarm.remove(mCheckupIntent);
- mAlarm.set(AlarmManager.RTC_WAKEUP, nextTime, mCheckupIntent);
- } else {
- if (localLOGV) Slog.v(TAG, "No need to schedule a memcheck alarm!");
- mAlarm.remove(mCheckupIntent);
- }
- }
-
- final PssStats mPssStats = new PssStats();
- final String[] mMemInfoFields = new String[] {
- "MemFree:", "Buffers:", "Cached:",
- "Active:", "Inactive:",
- "AnonPages:", "Mapped:", "Slab:",
- "SReclaimable:", "SUnreclaim:", "PageTables:" };
- final long[] mMemInfoSizes = new long[mMemInfoFields.length];
- final String[] mVMStatFields = new String[] {
- "pgfree ", "pgactivate ", "pgdeactivate ",
- "pgfault ", "pgmajfault " };
- final long[] mVMStatSizes = new long[mVMStatFields.length];
- final long[] mPrevVMStatSizes = new long[mVMStatFields.length];
- long mLastLogGlobalMemoryTime;
-
- void logGlobalMemory() {
- PssStats stats = mPssStats;
- mActivity.collectPss(stats);
- EventLog.writeEvent(EventLogTags.WATCHDOG_PSS_STATS,
- stats.mEmptyPss, stats.mEmptyCount,
- stats.mBackgroundPss, stats.mBackgroundCount,
- stats.mServicePss, stats.mServiceCount,
- stats.mVisiblePss, stats.mVisibleCount,
- stats.mForegroundPss, stats.mForegroundCount,
- stats.mNoPssCount);
- EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_STATS,
- stats.mProcDeaths[0], stats.mProcDeaths[1], stats.mProcDeaths[2],
- stats.mProcDeaths[3], stats.mProcDeaths[4]);
- Process.readProcLines("/proc/meminfo", mMemInfoFields, mMemInfoSizes);
- for (int i=0; i<mMemInfoSizes.length; i++) {
- mMemInfoSizes[i] *= 1024;
- }
- EventLog.writeEvent(EventLogTags.WATCHDOG_MEMINFO,
- (int)mMemInfoSizes[0], (int)mMemInfoSizes[1], (int)mMemInfoSizes[2],
- (int)mMemInfoSizes[3], (int)mMemInfoSizes[4],
- (int)mMemInfoSizes[5], (int)mMemInfoSizes[6], (int)mMemInfoSizes[7],
- (int)mMemInfoSizes[8], (int)mMemInfoSizes[9], (int)mMemInfoSizes[10]);
- long now = SystemClock.uptimeMillis();
- long dur = now - mLastLogGlobalMemoryTime;
- mLastLogGlobalMemoryTime = now;
- Process.readProcLines("/proc/vmstat", mVMStatFields, mVMStatSizes);
- for (int i=0; i<mVMStatSizes.length; i++) {
- long v = mVMStatSizes[i];
- mVMStatSizes[i] -= mPrevVMStatSizes[i];
- mPrevVMStatSizes[i] = v;
- }
- EventLog.writeEvent(EventLogTags.WATCHDOG_VMSTAT, dur,
- (int)mVMStatSizes[0], (int)mVMStatSizes[1], (int)mVMStatSizes[2],
- (int)mVMStatSizes[3], (int)mVMStatSizes[4]);
- }
-
void checkReboot(boolean fromAlarm) {
int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval
: Settings.Secure.getInt(
@@ -730,47 +360,6 @@ public class Watchdog extends Thread {
return null;
}
- /**
- * Compute the times during which we next would like to perform process
- * restarts.
- *
- * @param curTime The current system time.
- */
- void computeMemcheckTimesLocked(long curTime) {
- if (mMemcheckLastTime == curTime) {
- return;
- }
-
- mMemcheckLastTime = curTime;
-
- long memcheckExecStartTime = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_EXEC_START_TIME,
- MEMCHECK_DEFAULT_EXEC_START_TIME);
- long memcheckExecEndTime = Settings.Secure.getLong(
- mResolver, Settings.Secure.MEMCHECK_EXEC_END_TIME,
- MEMCHECK_DEFAULT_EXEC_END_TIME);
-
- mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
- memcheckExecEndTime);
- if (mMemcheckExecEndTime < curTime) {
- memcheckExecStartTime += 24*60*60;
- memcheckExecEndTime += 24*60*60;
- mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
- memcheckExecEndTime);
- }
- mMemcheckExecStartTime = computeCalendarTime(mCalendar, curTime,
- memcheckExecStartTime);
-
- if (localLOGV) {
- mCalendar.setTimeInMillis(curTime);
- Slog.v(TAG, "Current Time: " + mCalendar);
- mCalendar.setTimeInMillis(mMemcheckExecStartTime);
- Slog.v(TAG, "Start Check Time: " + mCalendar);
- mCalendar.setTimeInMillis(mMemcheckExecEndTime);
- Slog.v(TAG, "End Check Time: " + mCalendar);
- }
- }
-
static long computeCalendarTime(Calendar c, long curTime,
long secondsSinceMidnight) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 8d6ad93..0eca082 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -116,6 +116,8 @@ public class WifiService extends IWifiManager.Stub {
private final LockList mLocks = new LockList();
// some wifi lock statistics
+ private int mFullHighPerfLocksAcquired;
+ private int mFullHighPerfLocksReleased;
private int mFullLocksAcquired;
private int mFullLocksReleased;
private int mScanLocksAcquired;
@@ -169,6 +171,7 @@ public class WifiService extends IWifiManager.Stub {
private static final int MESSAGE_START_ACCESS_POINT = 6;
private static final int MESSAGE_STOP_ACCESS_POINT = 7;
private static final int MESSAGE_SET_CHANNELS = 8;
+ private static final int MESSAGE_ENABLE_NETWORKS = 9;
private final WifiHandler mWifiHandler;
@@ -631,6 +634,7 @@ public class WifiService extends IWifiManager.Stub {
}
public WifiConfiguration getWifiApConfiguration() {
+ enforceAccessPermission();
final ContentResolver cr = mContext.getContentResolver();
WifiConfiguration wifiConfig = new WifiConfiguration();
int authType;
@@ -648,7 +652,8 @@ public class WifiService extends IWifiManager.Stub {
}
}
- private void persistApConfiguration(WifiConfiguration wifiConfig) {
+ public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
+ enforceChangePermission();
final ContentResolver cr = mContext.getContentResolver();
boolean isWpa;
if (wifiConfig == null)
@@ -679,9 +684,9 @@ public class WifiService extends IWifiManager.Stub {
/* Configuration changed on a running access point */
if(enable && (wifiConfig != null)) {
try {
- persistApConfiguration(wifiConfig);
nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
SOFTAP_IFACE);
+ setWifiApConfiguration(wifiConfig);
return true;
} catch(Exception e) {
Slog.e(TAG, "Exception in nwService during AP restart");
@@ -717,7 +722,6 @@ public class WifiService extends IWifiManager.Stub {
wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
}
- persistApConfiguration(wifiConfig);
if (!mWifiStateTracker.loadDriver()) {
Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
@@ -734,6 +738,8 @@ public class WifiService extends IWifiManager.Stub {
return false;
}
+ setWifiApConfiguration(wifiConfig);
+
} else {
try {
@@ -1658,6 +1664,12 @@ public class WifiService extends IWifiManager.Stub {
mDeviceIdle = false;
mScreenOff = false;
mWifiStateTracker.enableRssiPolling(true);
+ /* DHCP or other temporary failures in the past can prevent
+ * a disabled network from being connected to, enable on screen on
+ */
+ if (mWifiStateTracker.isAnyNetworkDisabled()) {
+ sendEnableNetworksMessage();
+ }
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Slog.d(TAG, "ACTION_SCREEN_OFF");
mScreenOff = true;
@@ -1779,8 +1791,8 @@ public class WifiService extends IWifiManager.Stub {
msg.sendToTarget();
}
- private void sendStartMessage(boolean scanOnlyMode) {
- Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
+ private void sendStartMessage(int lockMode) {
+ Message.obtain(mWifiHandler, MESSAGE_START_WIFI, lockMode, 0).sendToTarget();
}
private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
@@ -1789,6 +1801,10 @@ public class WifiService extends IWifiManager.Stub {
uid, 0, wifiConfig).sendToTarget();
}
+ private void sendEnableNetworksMessage() {
+ Message.obtain(mWifiHandler, MESSAGE_ENABLE_NETWORKS).sendToTarget();
+ }
+
private void updateWifiState() {
// send a message so it's all serialized
Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
@@ -1798,12 +1814,15 @@ public class WifiService extends IWifiManager.Stub {
boolean wifiEnabled = getPersistedWifiEnabled();
boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
boolean lockHeld = mLocks.hasLocks();
- int strongestLockMode;
+ int strongestLockMode = WifiManager.WIFI_MODE_FULL;
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
- if (mDeviceIdle && lockHeld) {
+
+ if (lockHeld) {
strongestLockMode = mLocks.getStrongestLockMode();
- } else {
+ }
+ /* If device is not idle, lockmode cannot be scan only */
+ if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
strongestLockMode = WifiManager.WIFI_MODE_FULL;
}
@@ -1824,7 +1843,7 @@ public class WifiService extends IWifiManager.Stub {
sWakeLock.acquire();
sendEnableMessage(true, false, mLastEnableUid);
sWakeLock.acquire();
- sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
+ sendStartMessage(strongestLockMode);
} else if (!mWifiStateTracker.isDriverStopped()) {
int wakeLockTimeout =
Settings.Secure.getInt(
@@ -1902,8 +1921,10 @@ public class WifiService extends IWifiManager.Stub {
break;
case MESSAGE_START_WIFI:
- mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
+ mWifiStateTracker.setScanOnlyMode(msg.arg1 == WifiManager.WIFI_MODE_SCAN_ONLY);
mWifiStateTracker.restart();
+ mWifiStateTracker.setHighPerfMode(msg.arg1 ==
+ WifiManager.WIFI_MODE_FULL_HIGH_PERF);
sWakeLock.release();
break;
@@ -1946,6 +1967,10 @@ public class WifiService extends IWifiManager.Stub {
setNumAllowedChannelsBlocking(msg.arg1, msg.arg2 == 1);
break;
+ case MESSAGE_ENABLE_NETWORKS:
+ mWifiStateTracker.enableAllNetworks(getConfiguredNetworks());
+ break;
+
}
}
}
@@ -1983,8 +2008,10 @@ public class WifiService extends IWifiManager.Stub {
}
pw.println();
pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
+ mFullHighPerfLocksAcquired + " full high perf, " +
mScanLocksAcquired + " scan");
pw.println("Locks released: " + mFullLocksReleased + " full, " +
+ mFullHighPerfLocksReleased + " full high perf, " +
mScanLocksReleased + " scan");
pw.println();
pw.println("Locks held:");
@@ -2039,11 +2066,15 @@ public class WifiService extends IWifiManager.Stub {
if (mList.isEmpty()) {
return WifiManager.WIFI_MODE_FULL;
}
- for (WifiLock l : mList) {
- if (l.mMode == WifiManager.WIFI_MODE_FULL) {
- return WifiManager.WIFI_MODE_FULL;
- }
+
+ if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
+ return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
+ }
+
+ if (mFullLocksAcquired > mFullLocksReleased) {
+ return WifiManager.WIFI_MODE_FULL;
}
+
return WifiManager.WIFI_MODE_SCAN_ONLY;
}
@@ -2082,7 +2113,11 @@ public class WifiService extends IWifiManager.Stub {
public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
+ if (lockMode != WifiManager.WIFI_MODE_FULL &&
+ lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
+ lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
+ Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
+ if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
return false;
}
WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
@@ -2104,6 +2139,12 @@ public class WifiService extends IWifiManager.Stub {
++mFullLocksAcquired;
mBatteryStats.noteFullWifiLockAcquired(uid);
break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ ++mFullHighPerfLocksAcquired;
+ /* Treat high power as a full lock for battery stats */
+ mBatteryStats.noteFullWifiLockAcquired(uid);
+ break;
+
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksAcquired;
mBatteryStats.noteScanWifiLockAcquired(uid);
@@ -2143,6 +2184,10 @@ public class WifiService extends IWifiManager.Stub {
++mFullLocksReleased;
mBatteryStats.noteFullWifiLockReleased(uid);
break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ ++mFullHighPerfLocksReleased;
+ mBatteryStats.noteFullWifiLockReleased(uid);
+ break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksReleased;
mBatteryStats.noteScanWifiLockReleased(uid);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 68787cd..3841f75 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -16,11 +16,6 @@
package com.android.server;
-import static android.os.LocalPowerManager.CHEEK_EVENT;
-import static android.os.LocalPowerManager.OTHER_EVENT;
-import static android.os.LocalPowerManager.TOUCH_EVENT;
-import static android.os.LocalPowerManager.LONG_TOUCH_EVENT;
-import static android.os.LocalPowerManager.TOUCH_UP_EVENT;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
@@ -48,7 +43,6 @@ import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.WindowManagerPolicyThread;
-import com.android.server.KeyInputQueue.QueuedEvent;
import com.android.server.am.BatteryStatsService;
import android.Manifest;
@@ -63,17 +57,20 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
-import android.os.LatencyTimer;
import android.os.LocalPowerManager;
import android.os.Looper;
import android.os.Message;
@@ -93,34 +90,42 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.TypedValue;
import android.view.Display;
import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
import android.view.IApplicationToken;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.RawInputEvent;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
+import android.view.Surface.OutOfResourcesException;
import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
+import java.io.BufferedReader;
import java.io.BufferedWriter;
+import java.io.DataInputStream;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
@@ -135,7 +140,7 @@ import java.util.List;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback {
+ implements Watchdog.Monitor {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
@@ -156,16 +161,11 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean DEBUG_FREEZE = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
- static final boolean MEASURE_LATENCY = false;
- static private LatencyTimer lt;
static final boolean PROFILE_ORIENTATION = false;
static final boolean BLUR = true;
static final boolean localLOGV = DEBUG;
- /** How long to wait for subsequent key repeats, in milliseconds */
- static final int KEY_REPEAT_DELAY = 50;
-
/** How much to multiply the policy's type layer, to reserve room
* for multiple windows of the same type and Z-ordering adjustment
* with TYPE_LAYER_OFFSET. */
@@ -198,35 +198,19 @@ public class WindowManagerService extends IWindowManager.Stub
/** Adjustment to time to perform a dim, to make it more dramatic.
*/
static final int DIM_DURATION_MULTIPLIER = 6;
-
- static final int INJECT_FAILED = 0;
- static final int INJECT_SUCCEEDED = 1;
- static final int INJECT_NO_PERMISSION = -1;
+
+ // Maximum number of milliseconds to wait for input event injection.
+ // FIXME is this value reasonable?
+ private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
+
+ // Default input dispatching timeout in nanoseconds.
+ private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
static final int UPDATE_FOCUS_NORMAL = 0;
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
- /** The minimum time between dispatching touch events. */
- int mMinWaitTimeBetweenTouchEvents = 1000 / 35;
-
- // Last touch event time
- long mLastTouchEventTime = 0;
-
- // Last touch event type
- int mLastTouchEventType = OTHER_EVENT;
-
- // Time to wait before calling useractivity again. This saves CPU usage
- // when we get a flood of touch events.
- static final int MIN_TIME_BETWEEN_USERACTIVITIES = 1000;
-
- // Last time we call user activity
- long mLastUserActivityCallTime = 0;
-
- // Last time we updated battery stats
- long mLastBatteryStatsCallTime = 0;
-
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
@@ -349,7 +333,7 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Z-ordered (bottom-most first) list of all Window objects.
*/
- final ArrayList mWindows = new ArrayList();
+ final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
/**
* Windows that are being resized. Used so we can tell the client about
@@ -386,6 +370,7 @@ public class WindowManagerService extends IWindowManager.Stub
private DimAnimator mDimAnimator = null;
Surface mBlurSurface;
boolean mBlurShown;
+ Watermark mWatermark;
int mTransactionSequence = 0;
@@ -442,8 +427,6 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>();
final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>();
- //flag to detect fat touch events
- boolean mFatTouch = false;
Display mDisplay;
H mH = new H();
@@ -477,7 +460,6 @@ public class WindowManagerService extends IWindowManager.Stub
float mLastWallpaperY = -1;
float mLastWallpaperXStep = -1;
float mLastWallpaperYStep = -1;
- boolean mSendingPointersToWallpaper = false;
// This is set when we are waiting for a wallpaper to tell us it is done
// changing its scroll position.
WindowState mWaitingOnWallpaper;
@@ -495,12 +477,11 @@ public class WindowManagerService extends IWindowManager.Stub
float mWindowAnimationScale = 1.0f;
float mTransitionAnimationScale = 1.0f;
- final KeyWaiter mKeyWaiter = new KeyWaiter();
- final KeyQ mQueue;
- final InputDispatcherThread mInputThread;
+ final InputManager mInputManager;
// Who is holding the screen on.
Session mHoldingScreenOn;
+ PowerManager.WakeLock mHoldingScreenWakeLock;
boolean mTurnOnScreen;
@@ -511,8 +492,14 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mInTouchMode = false;
private ViewServer mViewServer;
+ private ArrayList<WindowChangeListener> mWindowChangeListeners =
+ new ArrayList<WindowChangeListener>();
+ private boolean mWindowsChanged = false;
- final Rect mTempRect = new Rect();
+ public interface WindowChangeListener {
+ public void windowsChanged();
+ public void focusChanged();
+ }
final Configuration mTempConfiguration = new Configuration();
int mScreenLayout = Configuration.SCREENLAYOUT_SIZE_UNDEFINED;
@@ -561,6 +548,7 @@ public class WindowManagerService extends IWindowManager.Stub
mHaveInputMethods);
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_DISPLAY);
+ android.os.Process.setCanSelfBackground(false);
synchronized (this) {
mService = s;
@@ -596,6 +584,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ android.os.Process.setCanSelfBackground(false);
mPolicy.init(mContext, mService, mPM);
synchronized (this) {
@@ -609,10 +598,6 @@ public class WindowManagerService extends IWindowManager.Stub
private WindowManagerService(Context context, PowerManagerService pm,
boolean haveInputMethods) {
- if (MEASURE_LATENCY) {
- lt = new LatencyTimer(100, 1000);
- }
-
mContext = context;
mHaveInputMethods = haveInputMethods;
mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -639,20 +624,11 @@ public class WindowManagerService extends IWindowManager.Stub
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter);
- int max_events_per_sec = 35;
- try {
- max_events_per_sec = Integer.parseInt(SystemProperties
- .get("windowsmgr.max_events_per_sec"));
- if (max_events_per_sec < 1) {
- max_events_per_sec = 35;
- }
- } catch (NumberFormatException e) {
- }
- mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
-
- mQueue = new KeyQ();
+ mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+ "KEEP_SCREEN_ON_FLAG");
+ mHoldingScreenWakeLock.setReferenceCounted(false);
- mInputThread = new InputDispatcherThread();
+ mInputManager = new InputManager(context, this);
PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
thr.start();
@@ -666,8 +642,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- mInputThread.start();
- mQueue.start();
+ mInputManager.start();
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
@@ -688,33 +663,35 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- private void placeWindowAfter(Object pos, WindowState window) {
+ private void placeWindowAfter(WindowState pos, WindowState window) {
final int i = mWindows.indexOf(pos);
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
TAG, "Adding window " + window + " at "
+ (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
mWindows.add(i+1, window);
+ mWindowsChanged = true;
}
- private void placeWindowBefore(Object pos, WindowState window) {
+ private void placeWindowBefore(WindowState pos, WindowState window) {
final int i = mWindows.indexOf(pos);
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
TAG, "Adding window " + window + " at "
+ i + " of " + mWindows.size() + " (before " + pos + ")");
mWindows.add(i, window);
+ mWindowsChanged = true;
}
//This method finds out the index of a window that has the same app token as
//win. used for z ordering the windows in mWindows
private int findIdxBasedOnAppTokens(WindowState win) {
//use a local variable to cache mWindows
- ArrayList localmWindows = mWindows;
+ ArrayList<WindowState> localmWindows = mWindows;
int jmax = localmWindows.size();
if(jmax == 0) {
return -1;
}
for(int j = (jmax-1); j >= 0; j--) {
- WindowState wentry = (WindowState)localmWindows.get(j);
+ WindowState wentry = localmWindows.get(j);
if(wentry.mAppToken == win.mAppToken) {
return j;
}
@@ -725,7 +702,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
- final ArrayList localmWindows = mWindows;
+ final ArrayList<WindowState> localmWindows = mWindows;
final int N = localmWindows.size();
final WindowState attached = win.mAttachedWindow;
@@ -759,6 +736,7 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Adding window " + win + " at "
+ (newIdx+1) + " of " + N);
localmWindows.add(newIdx+1, win);
+ mWindowsChanged = true;
}
}
}
@@ -769,7 +747,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Figure out where the window should go, based on the
// order of applications.
final int NA = mAppTokens.size();
- Object pos = null;
+ WindowState pos = null;
for (i=NA-1; i>=0; i--) {
AppWindowToken t = mAppTokens.get(i);
if (t == token) {
@@ -789,8 +767,7 @@ public class WindowManagerService extends IWindowManager.Stub
// we need to look some more.
if (pos != null) {
// Move behind any windows attached to this one.
- WindowToken atoken =
- mTokenMap.get(((WindowState)pos).mClient.asBinder());
+ WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
final int NC = atoken.windows.size();
if (NC > 0) {
@@ -816,8 +793,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (pos != null) {
// Move in front of any windows attached to this
// one.
- WindowToken atoken =
- mTokenMap.get(((WindowState)pos).mClient.asBinder());
+ WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
final int NC = atoken.windows.size();
if (NC > 0) {
@@ -832,7 +808,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Just search for the start of this layer.
final int myLayer = win.mBaseLayer;
for (i=0; i<N; i++) {
- WindowState w = (WindowState)localmWindows.get(i);
+ WindowState w = localmWindows.get(i);
if (w.mBaseLayer > myLayer) {
break;
}
@@ -841,6 +817,7 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Adding window " + win + " at "
+ i + " of " + N);
localmWindows.add(i, win);
+ mWindowsChanged = true;
}
}
}
@@ -848,7 +825,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Figure out where window should go, based on layer.
final int myLayer = win.mBaseLayer;
for (i=N-1; i>=0; i--) {
- if (((WindowState)localmWindows.get(i)).mBaseLayer <= myLayer) {
+ if (localmWindows.get(i).mBaseLayer <= myLayer) {
i++;
break;
}
@@ -858,6 +835,7 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Adding window " + win + " at "
+ i + " of " + N);
localmWindows.add(i, win);
+ mWindowsChanged = true;
}
if (addToToken) {
token.windows.add(tokenWindowsPos, win);
@@ -930,13 +908,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
- final ArrayList localmWindows = mWindows;
+ final ArrayList<WindowState> localmWindows = mWindows;
final int N = localmWindows.size();
WindowState w = null;
int i = N;
while (i > 0) {
i--;
- w = (WindowState)localmWindows.get(i);
+ w = localmWindows.get(i);
//Slog.i(TAG, "Checking window @" + i + " " + w + " fl=0x"
// + Integer.toHexString(w.mAttrs.flags));
@@ -951,7 +929,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (!willMove
&& w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& i > 0) {
- WindowState wb = (WindowState)localmWindows.get(i-1);
+ WindowState wb = localmWindows.get(i-1);
if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
i--;
w = wb;
@@ -981,7 +959,7 @@ public class WindowManagerService extends IWindowManager.Stub
int pos = 0;
pos = localmWindows.indexOf(curTarget);
while (pos >= 0) {
- WindowState win = (WindowState)localmWindows.get(pos);
+ WindowState win = localmWindows.get(pos);
if (win.mAppToken != token) {
break;
}
@@ -1066,6 +1044,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(
TAG, "Adding input method window " + win + " at " + pos);
mWindows.add(pos, win);
+ mWindowsChanged = true;
moveInputMethodDialogsLocked(pos+1);
return;
}
@@ -1085,7 +1064,7 @@ public class WindowManagerService extends IWindowManager.Stub
int wi = imw.mChildWindows.size();
while (wi > 0) {
wi--;
- WindowState cw = (WindowState)imw.mChildWindows.get(wi);
+ WindowState cw = imw.mChildWindows.get(wi);
cw.mAnimLayer = cw.mLayer + adj;
if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + cw
+ " anim layer: " + cw.mAnimLayer);
@@ -1107,10 +1086,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (wpos < interestingPos) interestingPos--;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Temp removing at " + wpos + ": " + win);
mWindows.remove(wpos);
+ mWindowsChanged = true;
int NC = win.mChildWindows.size();
while (NC > 0) {
NC--;
- WindowState cw = (WindowState)win.mChildWindows.get(NC);
+ WindowState cw = win.mChildWindows.get(NC);
int cpos = mWindows.indexOf(cw);
if (cpos >= 0) {
if (cpos < interestingPos) interestingPos--;
@@ -1133,6 +1113,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "ReAdd removing from " + wpos
+ ": " + win);
mWindows.remove(wpos);
+ mWindowsChanged = true;
reAddWindowLocked(wpos, win);
}
}
@@ -1161,7 +1142,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (pos >= 0) {
final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
if (pos < mWindows.size()) {
- WindowState wp = (WindowState)mWindows.get(pos);
+ WindowState wp = mWindows.get(pos);
if (wp == mInputMethodWindow) {
pos++;
}
@@ -1205,14 +1186,14 @@ public class WindowManagerService extends IWindowManager.Stub
// located here, and contiguous.
final int N = mWindows.size();
WindowState firstImWin = imPos < N
- ? (WindowState)mWindows.get(imPos) : null;
+ ? mWindows.get(imPos) : null;
// Figure out the actual input method window that should be
// at the bottom of their stack.
WindowState baseImWin = imWin != null
? imWin : mInputMethodDialogs.get(0);
if (baseImWin.mChildWindows.size() > 0) {
- WindowState cw = (WindowState)baseImWin.mChildWindows.get(0);
+ WindowState cw = baseImWin.mChildWindows.get(0);
if (cw.mSubLayer < 0) baseImWin = cw;
}
@@ -1221,7 +1202,7 @@ public class WindowManagerService extends IWindowManager.Stub
// First find the top IM window.
int pos = imPos+1;
while (pos < N) {
- if (!((WindowState)mWindows.get(pos)).mIsImWindow) {
+ if (!(mWindows.get(pos)).mIsImWindow) {
break;
}
pos++;
@@ -1229,7 +1210,7 @@ public class WindowManagerService extends IWindowManager.Stub
pos++;
// Now there should be no more input method windows above.
while (pos < N) {
- if (((WindowState)mWindows.get(pos)).mIsImWindow) {
+ if ((mWindows.get(pos)).mIsImWindow) {
break;
}
pos++;
@@ -1317,7 +1298,7 @@ public class WindowManagerService extends IWindowManager.Stub
// First find top-most window that has asked to be on top of the
// wallpaper; all wallpapers go behind it.
- final ArrayList localmWindows = mWindows;
+ final ArrayList<WindowState> localmWindows = mWindows;
int N = localmWindows.size();
WindowState w = null;
WindowState foundW = null;
@@ -1327,7 +1308,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = N;
while (i > 0) {
i--;
- w = (WindowState)localmWindows.get(i);
+ w = localmWindows.get(i);
if ((w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER)) {
if (topCurW == null) {
topCurW = w;
@@ -1498,7 +1479,7 @@ public class WindowManagerService extends IWindowManager.Stub
// AND any starting window associated with it, AND below the
// maximum layer the policy allows for wallpapers.
while (foundI > 0) {
- WindowState wb = (WindowState)localmWindows.get(foundI-1);
+ WindowState wb = localmWindows.get(foundI-1);
if (wb.mBaseLayer < maxLayer &&
wb.mAttachedWindow != foundW &&
(wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
@@ -1522,7 +1503,7 @@ public class WindowManagerService extends IWindowManager.Stub
} else {
// Okay i is the position immediately above the wallpaper. Look at
// what is below it for later.
- foundW = foundI > 0 ? (WindowState)localmWindows.get(foundI-1) : null;
+ foundW = foundI > 0 ? localmWindows.get(foundI-1) : null;
}
if (visible) {
@@ -1581,7 +1562,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (wallpaper == foundW) {
foundI--;
foundW = foundI > 0
- ? (WindowState)localmWindows.get(foundI-1) : null;
+ ? localmWindows.get(foundI-1) : null;
continue;
}
@@ -1593,6 +1574,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at "
+ oldIndex + ": " + wallpaper);
localmWindows.remove(oldIndex);
+ mWindowsChanged = true;
if (oldIndex < foundI) {
foundI--;
}
@@ -1604,6 +1586,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " from " + oldIndex + " to " + foundI);
localmWindows.add(foundI, wallpaper);
+ mWindowsChanged = true;
changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
}
}
@@ -1793,74 +1776,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
-
- void sendPointerToWallpaperLocked(WindowState srcWin,
- MotionEvent pointer, long eventTime) {
- int curTokenIndex = mWallpaperTokens.size();
- while (curTokenIndex > 0) {
- curTokenIndex--;
- WindowToken token = mWallpaperTokens.get(curTokenIndex);
- int curWallpaperIndex = token.windows.size();
- while (curWallpaperIndex > 0) {
- curWallpaperIndex--;
- WindowState wallpaper = token.windows.get(curWallpaperIndex);
- if ((wallpaper.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- continue;
- }
- try {
- MotionEvent ev = MotionEvent.obtainNoHistory(pointer);
- if (srcWin != null) {
- ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left,
- srcWin.mFrame.top-wallpaper.mFrame.top);
- } else {
- ev.offsetLocation(-wallpaper.mFrame.left, -wallpaper.mFrame.top);
- }
- switch (pointer.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mSendingPointersToWallpaper = true;
- break;
- case MotionEvent.ACTION_UP:
- mSendingPointersToWallpaper = false;
- break;
- }
- wallpaper.mClient.dispatchPointer(ev, eventTime, false);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure sending pointer to wallpaper", e);
- }
- }
- }
- }
-
- void dispatchPointerElsewhereLocked(WindowState srcWin, WindowState relWin,
- MotionEvent pointer, long eventTime, boolean skipped) {
- if (relWin != null) {
- mPolicy.dispatchedPointerEventLw(pointer, relWin.mFrame.left, relWin.mFrame.top);
- } else {
- mPolicy.dispatchedPointerEventLw(pointer, 0, 0);
- }
-
- // If we sent an initial down to the wallpaper, then continue
- // sending events until the final up.
- if (mSendingPointersToWallpaper) {
- if (skipped) {
- Slog.i(TAG, "Sending skipped pointer to wallpaper!");
- }
- sendPointerToWallpaperLocked(relWin, pointer, eventTime);
-
- // If we are on top of the wallpaper, then the wallpaper also
- // gets to see this movement.
- } else if (srcWin != null
- && pointer.getAction() == MotionEvent.ACTION_DOWN
- && mWallpaperTarget == srcWin
- && srcWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
- sendPointerToWallpaperLocked(relWin, pointer, eventTime);
- }
- }
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
- Rect outContentInsets) {
+ Rect outContentInsets, InputChannel outInputChannel) {
int res = mPolicy.checkAddPermission(attrs);
if (res != WindowManagerImpl.ADD_OKAY) {
return res;
@@ -1879,7 +1798,7 @@ public class WindowManagerService extends IWindowManager.Stub
mDisplay = wm.getDefaultDisplay();
mInitialDisplayWidth = mDisplay.getWidth();
mInitialDisplayHeight = mDisplay.getHeight();
- mQueue.setDisplay(mDisplay);
+ mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight);
reportNewConfig = true;
}
@@ -1972,6 +1891,15 @@ public class WindowManagerService extends IWindowManager.Stub
if (res != WindowManagerImpl.ADD_OKAY) {
return res;
}
+
+ if (outInputChannel != null) {
+ String name = win.makeInputChannelName();
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
+ win.mInputChannel = inputChannels[0];
+ inputChannels[1].transferToBinderOutParameter(outInputChannel);
+
+ mInputManager.registerInputChannel(win.mInputChannel);
+ }
// From now on, no exceptions or errors allowed!
@@ -2025,8 +1953,8 @@ public class WindowManagerService extends IWindowManager.Stub
boolean focusChanged = false;
if (win.canReceiveKeys()) {
- if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS))
- == true) {
+ focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);
+ if (focusChanged) {
imMayMove = false;
}
}
@@ -2042,10 +1970,9 @@ public class WindowManagerService extends IWindowManager.Stub
//dump();
if (focusChanged) {
- if (mCurrentFocus != null) {
- mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
- }
+ finishUpdateFocusedWindowAfterAssignLayersLocked();
}
+
if (localLOGV) Slog.v(
TAG, "New client " + client.asBinder()
+ ": window=" + win);
@@ -2087,6 +2014,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ ", surface=" + win.mSurface);
final long origId = Binder.clearCallingIdentity();
+
+ win.disposeInputChannel();
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Remove " + win + ": mSurface=" + win.mSurface
@@ -2147,12 +2076,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void removeWindowInnerLocked(Session session, WindowState win) {
- mKeyWaiter.finishedKey(session, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releaseMotionTarget(win);
- mKeyWaiter.releasePendingPointerLocked(win.mSession);
- mKeyWaiter.releasePendingTrackballLocked(win.mSession);
-
win.mRemoved = true;
if (mInputMethodTarget == win) {
@@ -2170,6 +2093,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWindowMap.remove(win.mClient.asBinder());
mWindows.remove(win);
+ mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
if (mInputMethodWindow == win) {
@@ -2230,6 +2154,8 @@ public class WindowManagerService extends IWindowManager.Stub
win.mAppToken.updateReportedVisibilityLocked();
}
}
+
+ mInputMonitor.updateInputWindowsLw();
}
private static void logSurface(WindowState w, String msg, RuntimeException where) {
@@ -2473,6 +2399,8 @@ public class WindowManagerService extends IWindowManager.Stub
outSurface.release();
}
} catch (Exception e) {
+ mInputMonitor.updateInputWindowsLw();
+
Slog.w(TAG, "Exception thrown when creating surface for client "
+ client + " (" + win.mAttrs.getTitle() + ")",
e);
@@ -2519,8 +2447,6 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win, transit, false)) {
focusMayChange = true;
win.mExiting = true;
- mKeyWaiter.finishedKey(session, client, true,
- KeyWaiter.RETURN_NOTHING);
} else if (win.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
@@ -2615,6 +2541,8 @@ public class WindowManagerService extends IWindowManager.Stub
TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
inTouchMode = mInTouchMode;
+
+ mInputMonitor.updateInputWindowsLw();
}
if (configChanged) {
@@ -2981,8 +2909,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.isVisibleNow()) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
changed = true;
}
}
@@ -3000,6 +2926,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ mInputMonitor.updateInputWindowsLw();
} else {
Slog.w(TAG, "Attempted to remove non-existing token: " + token);
}
@@ -3013,6 +2940,20 @@ public class WindowManagerService extends IWindowManager.Stub
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
+
+ // Get the dispatching timeout here while we are not holding any locks so that it
+ // can be cached by the AppWindowToken. The timeout value is used later by the
+ // input dispatcher in code that does hold locks. If we did not cache the value
+ // here we would run the chance of introducing a deadlock between the window manager
+ // (which holds locks while updating the input dispatcher state) and the activity manager
+ // (which holds locks while querying the application token).
+ long inputDispatchingTimeoutNanos;
+ try {
+ inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L;
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Could not get dispatching timeout.", ex);
+ inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3021,6 +2962,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
wtoken = new AppWindowToken(token);
+ wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
wtoken.groupId = groupId;
wtoken.appFullscreen = fullscreen;
wtoken.requestedOrientation = requestedOrientation;
@@ -3056,7 +2998,7 @@ public class WindowManagerService extends IWindowManager.Stub
public int getOrientationFromWindowsLocked() {
int pos = mWindows.size() - 1;
while (pos >= 0) {
- WindowState wtoken = (WindowState) mWindows.get(pos);
+ WindowState wtoken = mWindows.get(pos);
pos--;
if (wtoken.mAppToken != null) {
// We hit an application window. so the orientation will be determined by the
@@ -3284,7 +3226,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp);
changed = mFocusedApp != null;
mFocusedApp = null;
- mKeyWaiter.tickle();
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(null);
+ }
} else {
AppWindowToken newFocus = findAppWindowToken(token);
if (newFocus == null) {
@@ -3294,7 +3238,9 @@ public class WindowManagerService extends IWindowManager.Stub
changed = mFocusedApp != newFocus;
mFocusedApp = newFocus;
if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp);
- mKeyWaiter.tickle();
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(newFocus);
+ }
}
if (moveFocusNow && changed) {
@@ -3435,6 +3381,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Removing starting window: " + startingWindow);
mWindows.remove(startingWindow);
+ mWindowsChanged = true;
ttoken.windows.remove(startingWindow);
ttoken.allAppWindows.remove(startingWindow);
addWindowToListInOrderLocked(startingWindow, true);
@@ -3605,8 +3552,6 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
}
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
changed = true;
}
}
@@ -3634,6 +3579,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (performLayout) {
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
performLayoutAndPlaceSurfacesLocked();
+ } else {
+ mInputMonitor.updateInputWindowsLw();
}
}
}
@@ -3891,7 +3838,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken);
mFocusedApp = null;
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
- mKeyWaiter.tickle();
+ mInputMonitor.setFocusedAppLw(null);
}
} else {
Slog.w(TAG, "Attempted to remove non-existing app token: " + token);
@@ -3917,10 +3864,11 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState win = token.windows.get(i);
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Tmp removing app window " + win);
mWindows.remove(win);
+ mWindowsChanged = true;
int j = win.mChildWindows.size();
while (j > 0) {
j--;
- WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ WindowState cwin = win.mChildWindows.get(j);
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Tmp removing child window " + cwin);
mWindows.remove(cwin);
@@ -3948,7 +3896,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = NW;
while (i > 0) {
i--;
- WindowState win = (WindowState)mWindows.get(i);
+ WindowState win = mWindows.get(i);
if (win.getAppToken() != null) {
return i+1;
}
@@ -3974,7 +3922,7 @@ public class WindowManagerService extends IWindowManager.Stub
int j = win.mChildWindows.size();
while (j > 0) {
j--;
- WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ WindowState cwin = win.mChildWindows.get(j);
if (cwin.mSubLayer >= 0) {
for (int pos=NW-1; pos>=0; pos--) {
if (mWindows.get(pos) == cwin) {
@@ -4002,7 +3950,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int NCW = win.mChildWindows.size();
boolean added = false;
for (int j=0; j<NCW; j++) {
- WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ WindowState cwin = win.mChildWindows.get(j);
if (!added && cwin.mSubLayer >= 0) {
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding child window at "
+ index + ": " + cwin);
@@ -4021,6 +3969,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWindows.add(index, win);
index++;
}
+ mWindowsChanged = true;
return index;
}
@@ -4285,7 +4234,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void closeSystemDialogs(String reason) {
synchronized(mWindowMap) {
for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mSurface != null) {
try {
w.mClient.closeSystemDialogs(reason);
@@ -4356,7 +4305,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getSwitchState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return KeyInputQueue.getSwitchState(sw);
+ return mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getSwitchStateForDevice(int devid, int sw) {
@@ -4364,7 +4313,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getSwitchStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return KeyInputQueue.getSwitchState(devid, sw);
+ return mInputManager.getSwitchState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getScancodeState(int sw) {
@@ -4372,7 +4321,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getScancodeStateForDevice(int devid, int sw) {
@@ -4380,7 +4329,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getScancodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getScancodeState(devid, sw);
+ return mInputManager.getScanCodeState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getTrackballScancodeState(int sw) {
@@ -4388,7 +4337,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getTrackballScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getTrackballScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL, sw);
}
public int getDPadScancodeState(int sw) {
@@ -4396,7 +4345,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getDPadScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getDPadScancodeState(sw);
+ return mInputManager.getScanCodeState(-1, InputDevice.SOURCE_DPAD, sw);
}
public int getKeycodeState(int sw) {
@@ -4404,7 +4353,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, sw);
}
public int getKeycodeStateForDevice(int devid, int sw) {
@@ -4412,7 +4361,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getKeycodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getKeycodeState(devid, sw);
+ return mInputManager.getKeyCodeState(devid, InputDevice.SOURCE_ANY, sw);
}
public int getTrackballKeycodeState(int sw) {
@@ -4420,7 +4369,7 @@ public class WindowManagerService extends IWindowManager.Stub
"getTrackballKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getTrackballKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_TRACKBALL, sw);
}
public int getDPadKeycodeState(int sw) {
@@ -4428,11 +4377,27 @@ public class WindowManagerService extends IWindowManager.Stub
"getDPadKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
- return mQueue.getDPadKeycodeState(sw);
+ return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, sw);
}
-
+
public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
- return KeyInputQueue.hasKeys(keycodes, keyExists);
+ return mInputManager.hasKeys(-1, InputDevice.SOURCE_ANY, keycodes, keyExists);
+ }
+
+ public InputChannel monitorInput(String inputChannelName) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "monitorInput()")) {
+ throw new SecurityException("Requires READ_INPUT_STATE permission");
+ }
+ return mInputManager.monitorInput(inputChannelName);
+ }
+
+ public InputDevice getInputDevice(int deviceId) {
+ return mInputManager.getInputDevice(deviceId);
+ }
+
+ public int[] getInputDeviceIds() {
+ return mInputManager.getInputDeviceIds();
}
public void enableScreenAfterBoot() {
@@ -4469,7 +4434,7 @@ public class WindowManagerService extends IWindowManager.Stub
// have been drawn.
final int N = mWindows.size();
for (int i=0; i<N; i++) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.isVisibleLw() && !w.mObscured
&& (w.mOrientationChanging || !w.isDrawnLw())) {
return;
@@ -4577,12 +4542,12 @@ public class WindowManagerService extends IWindowManager.Stub
mLayoutNeeded = true;
startFreezingDisplayLocked();
Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags);
- mQueue.setOrientation(rotation);
+ mInputManager.setDisplayOrientation(0, rotation);
if (mDisplayEnabled) {
Surface.setOrientation(0, rotation, animFlags);
}
for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mSurface != null) {
w.mOrientationChanging = true;
}
@@ -4738,11 +4703,10 @@ public class WindowManagerService extends IWindowManager.Stub
boolean result = true;
- Object[] windows;
+ WindowState[] windows;
synchronized (mWindowMap) {
- windows = new Object[mWindows.size()];
//noinspection unchecked
- windows = mWindows.toArray(windows);
+ windows = mWindows.toArray(new WindowState[mWindows.size()]);
}
BufferedWriter out = null;
@@ -4754,7 +4718,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int count = windows.length;
for (int i = 0; i < count; i++) {
- final WindowState w = (WindowState) windows[i];
+ final WindowState w = windows[i];
out.write(Integer.toHexString(System.identityHashCode(w)));
out.write(' ');
out.append(w.mAttrs.getTitle());
@@ -4779,6 +4743,51 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
+ * Returns the focused window in the following format:
+ * windowHashCodeInHexadecimal windowName
+ *
+ * @param client The remote client to send the listing to.
+ * @return False if an error occurred, true otherwise.
+ */
+ boolean viewServerGetFocusedWindow(Socket client) {
+ if (isSystemSecure()) {
+ return false;
+ }
+
+ boolean result = true;
+
+ WindowState focusedWindow = getFocusedWindow();
+
+ BufferedWriter out = null;
+
+ // Any uncaught exception will crash the system process
+ try {
+ OutputStream clientStream = client.getOutputStream();
+ out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
+
+ if(focusedWindow != null) {
+ out.write(Integer.toHexString(System.identityHashCode(focusedWindow)));
+ out.write(' ');
+ out.append(focusedWindow.mAttrs.getTitle());
+ }
+ out.write('\n');
+ out.flush();
+ } catch (Exception e) {
+ result = false;
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ result = false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Sends a command to a target window. The result of the command, if any, will be
* written in the output stream of the specified socket.
*
@@ -4807,6 +4816,8 @@ public class WindowManagerService extends IWindowManager.Stub
Parcel data = null;
Parcel reply = null;
+ BufferedWriter out = null;
+
// Any uncaught exception will crash the system process
try {
// Find the hashcode of the window
@@ -4844,6 +4855,12 @@ public class WindowManagerService extends IWindowManager.Stub
reply.readException();
+ if (!client.isOutputShutdown()) {
+ out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
+ out.write("DONE\n");
+ out.flush();
+ }
+
} catch (Exception e) {
Slog.w(TAG, "Could not send command " + command + " with parameters " + parameters, e);
success = false;
@@ -4854,22 +4871,71 @@ public class WindowManagerService extends IWindowManager.Stub
if (reply != null) {
reply.recycle();
}
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+
+ }
+ }
}
return success;
}
+ public void addWindowChangeListener(WindowChangeListener listener) {
+ synchronized(mWindowMap) {
+ mWindowChangeListeners.add(listener);
+ }
+ }
+
+ public void removeWindowChangeListener(WindowChangeListener listener) {
+ synchronized(mWindowMap) {
+ mWindowChangeListeners.remove(listener);
+ }
+ }
+
+ private void notifyWindowsChanged() {
+ WindowChangeListener[] windowChangeListeners;
+ synchronized(mWindowMap) {
+ if(mWindowChangeListeners.isEmpty()) {
+ return;
+ }
+ windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()];
+ windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners);
+ }
+ int N = windowChangeListeners.length;
+ for(int i = 0; i < N; i++) {
+ windowChangeListeners[i].windowsChanged();
+ }
+ }
+
+ private void notifyFocusChanged() {
+ WindowChangeListener[] windowChangeListeners;
+ synchronized(mWindowMap) {
+ if(mWindowChangeListeners.isEmpty()) {
+ return;
+ }
+ windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()];
+ windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners);
+ }
+ int N = windowChangeListeners.length;
+ for(int i = 0; i < N; i++) {
+ windowChangeListeners[i].focusChanged();
+ }
+ }
+
private WindowState findWindow(int hashCode) {
if (hashCode == -1) {
return getFocusedWindow();
}
synchronized (mWindowMap) {
- final ArrayList windows = mWindows;
+ final ArrayList<WindowState> windows = mWindows;
final int count = windows.size();
for (int i = 0; i < count; i++) {
- WindowState w = (WindowState) windows.get(i);
+ WindowState w = windows.get(i);
if (System.identityHashCode(w) == hashCode) {
return w;
}
@@ -4908,7 +4974,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (mDisplay == null) {
return false;
}
- mQueue.getInputConfiguration(config);
+
+ mInputManager.getInputConfiguration(config);
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -4951,8 +5018,12 @@ public class WindowManagerService extends IWindowManager.Stub
mScreenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL
| Configuration.SCREENLAYOUT_LONG_NO;
} else {
- // Is this a large screen?
- if (longSize > 640 && shortSize >= 480) {
+ // What size is this screen screen?
+ if (longSize >= 800 && shortSize >= 600) {
+ // SVGA or larger screens at medium density are the point
+ // at which we consider it to be an extra large screen.
+ mScreenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ } else if (longSize >= 640 && shortSize >= 480) {
// VGA or larger screens at medium density are the point
// at which we consider it to be a large screen.
mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
@@ -4983,451 +5054,365 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.adjustConfigurationLw(config);
return true;
}
-
+
// -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
+
+ InputMonitor mInputMonitor = new InputMonitor();
+
+ /* Tracks the progress of input dispatch and ensures that input dispatch state
+ * is kept in sync with changes in window focus, visibility, registration, and
+ * other relevant Window Manager state transitions. */
+ final class InputMonitor {
+ // Current window with input focus for keys and other non-touch events. May be null.
+ private WindowState mInputFocus;
+
+ // When true, prevents input dispatch from proceeding until set to false again.
+ private boolean mInputDispatchFrozen;
+
+ // When true, input dispatch proceeds normally. Otherwise all events are dropped.
+ private boolean mInputDispatchEnabled = true;
- private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
- long curTime = SystemClock.uptimeMillis();
-
- if (eventType == TOUCH_EVENT || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
- if (mLastTouchEventType == eventType &&
- (curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) {
- return;
- }
- mLastUserActivityCallTime = curTime;
- mLastTouchEventType = eventType;
- }
-
- if (targetWin == null
- || targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
- mPowerManager.userActivity(curTime, false, eventType, false);
- }
- }
-
- // tells if it's a cheek event or not -- this function is stateful
- private static final int EVENT_NONE = 0;
- private static final int EVENT_UNKNOWN = 0;
- private static final int EVENT_CHEEK = 0;
- private static final int EVENT_IGNORE_DURATION = 300; // ms
- private static final float CHEEK_THRESHOLD = 0.6f;
- private int mEventState = EVENT_NONE;
- private float mEventSize;
-
- private int eventType(MotionEvent ev) {
- float size = ev.getSize();
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mEventSize = size;
- return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_EVENT;
- case MotionEvent.ACTION_UP:
- if (size > mEventSize) mEventSize = size;
- return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_UP_EVENT;
- case MotionEvent.ACTION_MOVE:
- final int N = ev.getHistorySize();
- if (size > mEventSize) mEventSize = size;
- if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
- for (int i=0; i<N; i++) {
- size = ev.getHistoricalSize(i);
- if (size > mEventSize) mEventSize = size;
- if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT;
- }
- if (ev.getEventTime() < ev.getDownTime() + EVENT_IGNORE_DURATION) {
- return TOUCH_EVENT;
- } else {
- return LONG_TOUCH_EVENT;
+ // Temporary list of windows information to provide to the input dispatcher.
+ private InputWindowList mTempInputWindows = new InputWindowList();
+
+ // Temporary input application object to provide to the input dispatcher.
+ private InputApplication mTempInputApplication = new InputApplication();
+
+ /* Notifies the window manager about a broken input channel.
+ *
+ * Called by the InputManager.
+ */
+ public void notifyInputChannelBroken(InputChannel inputChannel) {
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState == null) {
+ return; // irrelevant
+ }
+
+ Slog.i(TAG, "WINDOW DIED " + windowState);
+ removeWindowLocked(windowState.mSession, windowState);
}
- default:
- // not good
- return OTHER_EVENT;
}
- }
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
- if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Slog.v(TAG,
- "dispatchPointer " + ev);
-
- if (MEASURE_LATENCY) {
- lt.sample("3 Wait for last dispatch ", System.nanoTime() - qev.whenNano);
+
+ /* Notifies the window manager about an input channel that is not responding.
+ * The method can either cause dispatching to be aborted by returning -2 or
+ * return a new timeout in nanoseconds.
+ *
+ * Called by the InputManager.
+ */
+ public long notifyInputChannelANR(InputChannel inputChannel) {
+ AppWindowToken token;
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState == null) {
+ return -2; // irrelevant, abort dispatching (-2)
+ }
+
+ Slog.i(TAG, "Input event dispatching timed out sending to "
+ + windowState.mAttrs.getTitle());
+ token = windowState.mAppToken;
+ }
+
+ return notifyANRInternal(token);
}
-
- Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, true, false, pid, uid);
-
- if (MEASURE_LATENCY) {
- lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano);
+
+ /* Notifies the window manager about an input channel spontaneously recovering from ANR
+ * by successfully delivering the event that originally timed out.
+ *
+ * Called by the InputManager.
+ */
+ public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+ // Nothing to do just now.
+ // Just wait for the user to dismiss the ANR dialog.
}
+
+ /* Notifies the window manager about an application that is not responding
+ * in general rather than with respect to a particular input channel.
+ * The method can either cause dispatching to be aborted by returning -2 or
+ * return a new timeout in nanoseconds.
+ *
+ * Called by the InputManager.
+ */
+ public long notifyANR(Object token) {
+ AppWindowToken appWindowToken = (AppWindowToken) token;
- int action = ev.getAction();
-
- if (action == MotionEvent.ACTION_UP) {
- // let go of our target
- mKeyWaiter.mMotionTarget = null;
- mPowerManager.logPointerUpEvent();
- } else if (action == MotionEvent.ACTION_DOWN) {
- mPowerManager.logPointerDownEvent();
+ Slog.i(TAG, "Input event dispatching timed out sending to application "
+ + appWindowToken.stringName);
+ return notifyANRInternal(appWindowToken);
}
-
- if (targetObj == null) {
- // In this case we are either dropping the event, or have received
- // a move or up without a down. It is common to receive move
- // events in such a way, since this means the user is moving the
- // pointer without actually pressing down. All other cases should
- // be atypical, so let's log them.
- if (action != MotionEvent.ACTION_MOVE) {
- Slog.w(TAG, "No window to dispatch pointer action " + ev.getAction());
- }
- synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true);
- }
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ private long notifyANRInternal(AppWindowToken token) {
+ if (token != null && token.appToken != null) {
+ try {
+ // Notify the activity manager about the timeout and let it decide whether
+ // to abort dispatching or keep waiting.
+ boolean abort = token.appToken.keyDispatchingTimedOut();
+ if (! abort) {
+ // The activity manager declined to abort dispatching.
+ // Wait a bit longer and timeout again later.
+ return token.inputDispatchingTimeoutNanos;
+ }
+ } catch (RemoteException ex) {
+ }
}
- ev.recycle();
- return INJECT_FAILED;
+ return -2; // abort dispatching
}
- if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
+
+ private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true);
+ return getWindowStateForInputChannelLocked(inputChannel);
}
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_SUCCEEDED;
}
-
- WindowState target = (WindowState)targetObj;
-
- final long eventTime = ev.getEventTime();
- final long eventTimeNano = ev.getEventTimeNano();
-
- //Slog.i(TAG, "Sending " + ev + " to " + target);
-
- if (uid != 0 && uid != target.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting pointer event from pid "
- + pid + " uid " + uid + " to window " + target
- + " owned by uid " + target.mSession.mUid);
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
+ int windowCount = mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState windowState = mWindows.get(i);
+ if (windowState.mInputChannel == inputChannel) {
+ return windowState;
}
- ev.recycle();
- return INJECT_NO_PERMISSION;
}
+
+ return null;
}
-
- if (MEASURE_LATENCY) {
- lt.sample("4 in dispatchPointer ", System.nanoTime() - eventTimeNano);
- }
-
- if ((target.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
- //target wants to ignore fat touch events
- boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev);
- //explicit flag to return without processing event further
- boolean returnFlag = false;
- if((action == MotionEvent.ACTION_DOWN)) {
- mFatTouch = false;
- if(cheekPress) {
- mFatTouch = true;
- returnFlag = true;
+
+ /* Updates the cached window information provided to the input dispatcher. */
+ public void updateInputWindowsLw() {
+ // Populate the input window list with information about all of the windows that
+ // could potentially receive input.
+ // As an optimization, we could try to prune the list of windows but this turns
+ // out to be difficult because only the native code knows for sure which window
+ // currently has touch focus.
+ final ArrayList<WindowState> windows = mWindows;
+ final int N = windows.size();
+ for (int i = N - 1; i >= 0; i--) {
+ final WindowState child = windows.get(i);
+ if (child.mInputChannel == null || child.mRemoved) {
+ // Skip this window because it cannot possibly receive input.
+ continue;
}
- } else {
- if(action == MotionEvent.ACTION_UP) {
- if(mFatTouch) {
- //earlier even was invalid doesnt matter if current up is cheekpress or not
- mFatTouch = false;
- returnFlag = true;
- } else if(cheekPress) {
- //cancel the earlier event
- ev.setAction(MotionEvent.ACTION_CANCEL);
- action = MotionEvent.ACTION_CANCEL;
+
+ final int flags = child.mAttrs.flags;
+ final int type = child.mAttrs.type;
+
+ final boolean hasFocus = (child == mInputFocus);
+ final boolean isVisible = child.isVisibleLw();
+ final boolean hasWallpaper = (child == mWallpaperTarget)
+ && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
+
+ // Add a window to our list of input windows.
+ final InputWindow inputWindow = mTempInputWindows.add();
+ inputWindow.inputChannel = child.mInputChannel;
+ inputWindow.layoutParamsFlags = flags;
+ inputWindow.layoutParamsType = type;
+ inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
+ inputWindow.visible = isVisible;
+ inputWindow.hasFocus = hasFocus;
+ inputWindow.hasWallpaper = hasWallpaper;
+ inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindow.ownerPid = child.mSession.mPid;
+ inputWindow.ownerUid = child.mSession.mUid;
+
+ final Rect frame = child.mFrame;
+ inputWindow.frameLeft = frame.left;
+ inputWindow.frameTop = frame.top;
+
+ switch (child.mTouchableInsets) {
+ default:
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ inputWindow.touchableAreaLeft = frame.left;
+ inputWindow.touchableAreaTop = frame.top;
+ inputWindow.touchableAreaRight = frame.right;
+ inputWindow.touchableAreaBottom = frame.bottom;
+ break;
+
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
+ Rect inset = child.mGivenContentInsets;
+ inputWindow.touchableAreaLeft = frame.left + inset.left;
+ inputWindow.touchableAreaTop = frame.top + inset.top;
+ inputWindow.touchableAreaRight = frame.right - inset.right;
+ inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
+ break;
}
- } else if(action == MotionEvent.ACTION_MOVE) {
- if(mFatTouch) {
- //two cases here
- //an invalid down followed by 0 or moves(valid or invalid)
- //a valid down, invalid move, more moves. want to ignore till up
- returnFlag = true;
- } else if(cheekPress) {
- //valid down followed by invalid moves
- //an invalid move have to cancel earlier action
- ev.setAction(MotionEvent.ACTION_CANCEL);
- action = MotionEvent.ACTION_CANCEL;
- if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE");
- //note that the subsequent invalid moves will not get here
- mFatTouch = true;
+
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
+ Rect inset = child.mGivenVisibleInsets;
+ inputWindow.touchableAreaLeft = frame.left + inset.left;
+ inputWindow.touchableAreaTop = frame.top + inset.top;
+ inputWindow.touchableAreaRight = frame.right - inset.right;
+ inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
+ break;
}
}
- } //else if action
- if(returnFlag) {
- //recycle que, ev
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_FAILED;
}
- } //end if target
- // Enable this for testing the "right" value
- if (false && action == MotionEvent.ACTION_DOWN) {
- int max_events_per_sec = 35;
- try {
- max_events_per_sec = Integer.parseInt(SystemProperties
- .get("windowsmgr.max_events_per_sec"));
- if (max_events_per_sec < 1) {
- max_events_per_sec = 35;
- }
- } catch (NumberFormatException e) {
+ // Send windows to native code.
+ mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
+
+ // Clear the list in preparation for the next round.
+ // Also avoids keeping InputChannel objects referenced unnecessarily.
+ mTempInputWindows.clear();
+ }
+
+ /* Provides feedback for a virtual key down. */
+ public void virtualKeyDownFeedback() {
+ synchronized (mWindowMap) {
+ mPolicy.performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
}
- mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
}
-
- /*
- * Throttle events to minimize CPU usage when there's a flood of events
- * e.g. constant contact with the screen
+
+ /* Notifies that an app switch key (BACK / HOME) has just been pressed.
+ * This essentially starts a .5 second timeout for the application to process
+ * subsequent input events while waiting for the app switch to occur. If it takes longer
+ * than this, the pending events will be dropped.
*/
- if (action == MotionEvent.ACTION_MOVE) {
- long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents;
- long now = SystemClock.uptimeMillis();
- if (now < nextEventTime) {
- try {
- Thread.sleep(nextEventTime - now);
- } catch (InterruptedException e) {
- }
- mLastTouchEventTime = nextEventTime;
- } else {
- mLastTouchEventTime = now;
- }
+ public void notifyAppSwitchComing() {
+ // TODO Not implemented yet. Should go in the native side.
}
-
- if (MEASURE_LATENCY) {
- lt.sample("5 in dispatchPointer ", System.nanoTime() - eventTimeNano);
+
+ /* Notifies that the lid switch changed state. */
+ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+ mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
}
-
- synchronized(mWindowMap) {
- if (!target.isVisibleLw()) {
- // During this motion dispatch, the target window has become
- // invisible.
- dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), false);
- if (qev != null) {
- mQueue.recycleEvent(qev);
- }
- ev.recycle();
- return INJECT_SUCCEEDED;
- }
-
- if (qev != null && action == MotionEvent.ACTION_MOVE) {
- mKeyWaiter.bindTargetWindowLocked(target,
- KeyWaiter.RETURN_PENDING_POINTER, qev);
- ev = null;
- } else {
- if (action == MotionEvent.ACTION_DOWN) {
- WindowState out = mKeyWaiter.mOutsideTouchTargets;
- if (out != null) {
- MotionEvent oev = MotionEvent.obtain(ev);
- oev.setAction(MotionEvent.ACTION_OUTSIDE);
- do {
- final Rect frame = out.mFrame;
- oev.offsetLocation(-(float)frame.left, -(float)frame.top);
- try {
- out.mClient.dispatchPointer(oev, eventTime, false);
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during outside motion dispatch: " + out);
- }
- oev.offsetLocation((float)frame.left, (float)frame.top);
- out = out.mNextOutsideTouch;
- } while (out != null);
- mKeyWaiter.mOutsideTouchTargets = null;
+
+ /* Provides an opportunity for the window manager policy to intercept early key
+ * processing as soon as the key has been read from the device. */
+ public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
+ int policyFlags, boolean isScreenOn) {
+ return mPolicy.interceptKeyBeforeQueueing(whenNanos,
+ keyCode, down, policyFlags, isScreenOn);
+ }
+
+ /* Provides an opportunity for the window manager policy to process a key before
+ * ordinary dispatch. */
+ public boolean interceptKeyBeforeDispatching(InputChannel focus,
+ int action, int flags, int keyCode, int metaState, int repeatCount,
+ int policyFlags) {
+ WindowState windowState = getWindowStateForInputChannel(focus);
+ return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags,
+ keyCode, metaState, repeatCount, policyFlags);
+ }
+
+ /* Called when the current input focus changes.
+ * Layer assignment is assumed to be complete by the time this is called.
+ */
+ public void setInputFocusLw(WindowState newWindow) {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "Input focus has changed to " + newWindow);
+ }
+
+ if (newWindow != mInputFocus) {
+ if (newWindow != null && newWindow.canReceiveKeys()) {
+ // If the new input focus is an error window or appears above the current
+ // input focus, preempt any pending synchronous dispatch so that we can
+ // start delivering events to the new input focus as soon as possible.
+ if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "New SYSTEM_ERROR window; resetting state");
+ }
+ preemptInputDispatchLw();
+ } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Transferring focus to new window at higher layer: "
+ + "old win layer=" + mInputFocus.mLayer
+ + ", new win layer=" + newWindow.mLayer);
+ }
+ preemptInputDispatchLw();
}
+
+ // Displaying a window implicitly causes dispatching to be unpaused.
+ // This is to protect against bugs if someone pauses dispatching but
+ // forgets to resume.
+ newWindow.mToken.paused = false;
}
-
- dispatchPointerElsewhereLocked(target, null, ev, ev.getEventTime(), false);
-
- final Rect frame = target.mFrame;
- ev.offsetLocation(-(float)frame.left, -(float)frame.top);
- mKeyWaiter.bindTargetWindowLocked(target);
+
+ mInputFocus = newWindow;
+ updateInputWindowsLw();
}
}
-
- // finally offset the event to the target's coordinate system and
- // dispatch the event.
- try {
- if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) {
- Slog.v(TAG, "Delivering pointer " + qev + " to " + target);
- }
-
- if (MEASURE_LATENCY) {
- lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano);
- }
-
- target.mClient.dispatchPointer(ev, eventTime, true);
-
- if (MEASURE_LATENCY) {
- lt.sample("7 after svr->client ipc ", System.nanoTime() - eventTimeNano);
- }
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during motion dispatch: " + target);
- mKeyWaiter.mMotionTarget = null;
- try {
- removeWindow(target.mSession, target.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
- }
+
+ /* Tells the dispatcher to stop waiting for its current synchronous event targets.
+ * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
+ * can begin.
+ */
+ private void preemptInputDispatchLw() {
+ mInputManager.preemptInputDispatch();
}
- return INJECT_FAILED;
- }
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
-
- Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
- ev, false, false, pid, uid);
- if (focusObj == null) {
- Slog.w(TAG, "No focus window, dropping trackball: " + ev);
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ public void setFocusedAppLw(AppWindowToken newApp) {
+ // Focused app has changed.
+ if (newApp == null) {
+ mInputManager.setFocusedApplication(null);
+ } else {
+ mTempInputApplication.name = newApp.toString();
+ mTempInputApplication.dispatchingTimeoutNanos =
+ newApp.inputDispatchingTimeoutNanos;
+ mTempInputApplication.token = newApp;
+
+ mInputManager.setFocusedApplication(mTempInputApplication);
}
- ev.recycle();
- return INJECT_FAILED;
}
- if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ public void pauseDispatchingLw(WindowToken window) {
+ if (! window.paused) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Pausing WindowToken " + window);
+ }
+
+ window.paused = true;
+ updateInputWindowsLw();
}
- ev.recycle();
- return INJECT_SUCCEEDED;
}
-
- WindowState focus = (WindowState)focusObj;
-
- if (uid != 0 && uid != focus.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting key event from pid "
- + pid + " uid " + uid + " to window " + focus
- + " owned by uid " + focus.mSession.mUid);
- if (qev != null) {
- mQueue.recycleEvent(qev);
+
+ public void resumeDispatchingLw(WindowToken window) {
+ if (window.paused) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Resuming WindowToken " + window);
}
- ev.recycle();
- return INJECT_NO_PERMISSION;
+
+ window.paused = false;
+ updateInputWindowsLw();
}
}
-
- final long eventTime = ev.getEventTime();
-
- synchronized(mWindowMap) {
- if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) {
- mKeyWaiter.bindTargetWindowLocked(focus,
- KeyWaiter.RETURN_PENDING_TRACKBALL, qev);
- // We don't deliver movement events to the client, we hold
- // them and wait for them to call back.
- ev = null;
- } else {
- mKeyWaiter.bindTargetWindowLocked(focus);
+
+ public void freezeInputDispatchingLw() {
+ if (! mInputDispatchFrozen) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Freezing input dispatching");
+ }
+
+ mInputDispatchFrozen = true;
+ updateInputDispatchModeLw();
}
}
-
- try {
- focus.mClient.dispatchTrackball(ev, eventTime, true);
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus);
- try {
- removeWindow(focus.mSession, focus.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
+
+ public void thawInputDispatchingLw() {
+ if (mInputDispatchFrozen) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Thawing input dispatching");
+ }
+
+ mInputDispatchFrozen = false;
+ updateInputDispatchModeLw();
}
}
-
- return INJECT_FAILED;
- }
-
- /**
- * @return Returns true if event was dispatched, false if it was dropped for any reason
- */
- private int dispatchKey(KeyEvent event, int pid, int uid) {
- if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
-
- Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
- null, false, false, pid, uid);
- if (focusObj == null) {
- Slog.w(TAG, "No focus window, dropping: " + event);
- return INJECT_FAILED;
- }
- if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
- return INJECT_SUCCEEDED;
- }
-
- // Okay we have finished waiting for the last event to be processed.
- // First off, if this is a repeat event, check to see if there is
- // a corresponding up event in the queue. If there is, we will
- // just drop the repeat, because it makes no sense to repeat after
- // the user has released a key. (This is especially important for
- // long presses.)
- if (event.getRepeatCount() > 0 && mQueue.hasKeyUpEvent(event)) {
- return INJECT_SUCCEEDED;
- }
-
- WindowState focus = (WindowState)focusObj;
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Dispatching to " + focus + ": " + event);
-
- if (uid != 0 && uid != focus.mSession.mUid) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, pid, uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting key event from pid "
- + pid + " uid " + uid + " to window " + focus
- + " owned by uid " + focus.mSession.mUid);
- return INJECT_NO_PERMISSION;
+
+ public void setEventDispatchingLw(boolean enabled) {
+ if (mInputDispatchEnabled != enabled) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Setting event dispatching to " + enabled);
+ }
+
+ mInputDispatchEnabled = enabled;
+ updateInputDispatchModeLw();
}
}
-
- synchronized(mWindowMap) {
- mKeyWaiter.bindTargetWindowLocked(focus);
- }
-
- // NOSHIP extra state logging
- mKeyWaiter.recordDispatchState(event, focus);
- // END NOSHIP
-
- try {
- if (DEBUG_INPUT || DEBUG_FOCUS) {
- Slog.v(TAG, "Delivering key " + event.getKeyCode()
- + " to " + focus);
- }
- focus.mClient.dispatchKey(event);
- return INJECT_SUCCEEDED;
- } catch (android.os.RemoteException e) {
- Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus);
- try {
- removeWindow(focus.mSession, focus.mClient);
- } catch (java.util.NoSuchElementException ex) {
- // This will happen if the window has already been
- // removed.
- }
+
+ private void updateInputDispatchModeLw() {
+ mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
-
- return INJECT_FAILED;
}
public void pauseKeyDispatching(IBinder _token) {
@@ -5439,7 +5424,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- mKeyWaiter.pauseDispatchingLocked(token);
+ mInputMonitor.pauseDispatchingLw(token);
}
}
}
@@ -5453,7 +5438,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- mKeyWaiter.resumeDispatchingLocked(token);
+ mInputMonitor.resumeDispatchingLw(token);
}
}
}
@@ -5465,12 +5450,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized (mWindowMap) {
- mKeyWaiter.setEventDispatchingLocked(enabled);
+ mInputMonitor.setEventDispatchingLw(enabled);
}
}
/**
* Injects a keystroke event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the keystroke action. (Be sure to use
* {@link SystemClock#uptimeMillis()} as the timebase.)
@@ -5487,33 +5474,35 @@ public class WindowManagerService extends IWindowManager.Stub
int metaState = ev.getMetaState();
int deviceId = ev.getDeviceId();
int scancode = ev.getScanCode();
+ int source = ev.getSource();
+
+ if (source == InputDevice.SOURCE_UNKNOWN) {
+ source = InputDevice.SOURCE_KEYBOARD;
+ }
if (eventTime == 0) eventTime = SystemClock.uptimeMillis();
if (downTime == 0) downTime = eventTime;
KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
- deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM);
+ deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM, source);
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result = dispatchKey(newEvent, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
- }
+
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
+
Binder.restoreCallingIdentity(ident);
- switch (result) {
- case INJECT_NO_PERMISSION:
- throw new SecurityException(
- "Injecting to another application requires INJECT_EVENTS permission");
- case INJECT_SUCCEEDED:
- return true;
- }
- return false;
+ return reportInjectionResult(result);
}
/**
* Inject a pointer (touch) event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the pointer (touch) action. (As noted in
* {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
@@ -5525,23 +5514,25 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result = dispatchPointer(null, ev, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+
+ MotionEvent newEvent = MotionEvent.obtain(ev);
+ if ((newEvent.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
+ newEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
}
+
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
+
Binder.restoreCallingIdentity(ident);
- switch (result) {
- case INJECT_NO_PERMISSION:
- throw new SecurityException(
- "Injecting to another application requires INJECT_EVENTS permission");
- case INJECT_SUCCEEDED:
- return true;
- }
- return false;
+ return reportInjectionResult(result);
}
/**
* Inject a trackball (navigation device) event into the UI.
+ * Even when sync is false, this method may block while waiting for current
+ * input events to be dispatched.
*
* @param ev A motion event describing the trackball action. (As noted in
* {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
@@ -5553,19 +5544,59 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
- final int result = dispatchTrackball(null, ev, pid, uid);
- if (sync) {
- mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
+
+ MotionEvent newEvent = MotionEvent.obtain(ev);
+ if ((newEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) == 0) {
+ newEvent.setSource(InputDevice.SOURCE_TRACKBALL);
}
+
+ final int result = mInputManager.injectInputEvent(newEvent, pid, uid,
+ sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH
+ : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ INJECTION_TIMEOUT_MILLIS);
+
Binder.restoreCallingIdentity(ident);
+ return reportInjectionResult(result);
+ }
+
+ /**
+ * Inject an input event into the UI without waiting for dispatch to commence.
+ * This variant is useful for fire-and-forget input event injection. It does not
+ * block any longer than it takes to enqueue the input event.
+ *
+ * @param ev An input event. (Be sure to set the input source correctly.)
+ * @return Returns true if event was dispatched, false if it was dropped for any reason
+ */
+ public boolean injectInputEventNoWait(InputEvent ev) {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+
+ final int result = mInputManager.injectInputEvent(ev, pid, uid,
+ InputManager.INPUT_EVENT_INJECTION_SYNC_NONE,
+ INJECTION_TIMEOUT_MILLIS);
+
+ Binder.restoreCallingIdentity(ident);
+ return reportInjectionResult(result);
+ }
+
+ private boolean reportInjectionResult(int result) {
switch (result) {
- case INJECT_NO_PERMISSION:
+ case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ Slog.w(TAG, "Input event injection permission denied.");
throw new SecurityException(
"Injecting to another application requires INJECT_EVENTS permission");
- case INJECT_SUCCEEDED:
+ case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
+ //Slog.v(TAG, "Input event injection succeeded.");
return true;
+ case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
+ Slog.w(TAG, "Input event injection timed out.");
+ return false;
+ case InputManager.INPUT_EVENT_INJECTION_FAILED:
+ default:
+ Slog.w(TAG, "Input event injection failed.");
+ return false;
}
- return false;
}
private WindowState getFocusedWindow() {
@@ -5578,894 +5609,6 @@ public class WindowManagerService extends IWindowManager.Stub
return mCurrentFocus;
}
- /**
- * This class holds the state for dispatching key events. This state
- * is protected by the KeyWaiter instance, NOT by the window lock. You
- * can be holding the main window lock while acquire the KeyWaiter lock,
- * but not the other way around.
- */
- final class KeyWaiter {
- // NOSHIP debugging
- public class DispatchState {
- private KeyEvent event;
- private WindowState focus;
- private long time;
- private WindowState lastWin;
- private IBinder lastBinder;
- private boolean finished;
- private boolean gotFirstWindow;
- private boolean eventDispatching;
- private long timeToSwitch;
- private boolean wasFrozen;
- private boolean focusPaused;
- private WindowState curFocus;
-
- DispatchState(KeyEvent theEvent, WindowState theFocus) {
- focus = theFocus;
- event = theEvent;
- time = System.currentTimeMillis();
- // snapshot KeyWaiter state
- lastWin = mLastWin;
- lastBinder = mLastBinder;
- finished = mFinished;
- gotFirstWindow = mGotFirstWindow;
- eventDispatching = mEventDispatching;
- timeToSwitch = mTimeToSwitch;
- wasFrozen = mWasFrozen;
- curFocus = mCurrentFocus;
- // cache the paused state at ctor time as well
- if (theFocus == null || theFocus.mToken == null) {
- focusPaused = false;
- } else {
- focusPaused = theFocus.mToken.paused;
- }
- }
-
- public String toString() {
- return "{{" + event + " to " + focus + " @ " + time
- + " lw=" + lastWin + " lb=" + lastBinder
- + " fin=" + finished + " gfw=" + gotFirstWindow
- + " ed=" + eventDispatching + " tts=" + timeToSwitch
- + " wf=" + wasFrozen + " fp=" + focusPaused
- + " mcf=" + curFocus + "}}";
- }
- };
- private DispatchState mDispatchState = null;
- public void recordDispatchState(KeyEvent theEvent, WindowState theFocus) {
- mDispatchState = new DispatchState(theEvent, theFocus);
- }
- // END NOSHIP
-
- public static final int RETURN_NOTHING = 0;
- public static final int RETURN_PENDING_POINTER = 1;
- public static final int RETURN_PENDING_TRACKBALL = 2;
-
- final Object SKIP_TARGET_TOKEN = new Object();
- final Object CONSUMED_EVENT_TOKEN = new Object();
-
- private WindowState mLastWin = null;
- private IBinder mLastBinder = null;
- private boolean mFinished = true;
- private boolean mGotFirstWindow = false;
- private boolean mEventDispatching = true;
- private long mTimeToSwitch = 0;
- /* package */ boolean mWasFrozen = false;
-
- // Target of Motion events
- WindowState mMotionTarget;
-
- // Windows above the target who would like to receive an "outside"
- // touch event for any down events outside of them.
- WindowState mOutsideTouchTargets;
-
- /**
- * Wait for the last event dispatch to complete, then find the next
- * target that should receive the given event and wait for that one
- * to be ready to receive it.
- */
- Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
- MotionEvent nextMotion, boolean isPointerEvent,
- boolean failIfTimeout, int callingPid, int callingUid) {
- long startTime = SystemClock.uptimeMillis();
- long keyDispatchingTimeout = 5 * 1000;
- long waitedFor = 0;
-
- while (true) {
- // Figure out which window we care about. It is either the
- // last window we are waiting to have process the event or,
- // if none, then the next window we think the event should go
- // to. Note: we retrieve mLastWin outside of the lock, so
- // it may change before we lock. Thus we must check it again.
- WindowState targetWin = mLastWin;
- boolean targetIsNew = targetWin == null;
- if (DEBUG_INPUT) Slog.v(
- TAG, "waitForLastKey: mFinished=" + mFinished +
- ", mLastWin=" + mLastWin);
- if (targetIsNew) {
- Object target = findTargetWindow(nextKey, qev, nextMotion,
- isPointerEvent, callingPid, callingUid);
- if (target == SKIP_TARGET_TOKEN) {
- // The user has pressed a special key, and we are
- // dropping all pending events before it.
- if (DEBUG_INPUT) Slog.v(TAG, "Skipping: " + nextKey
- + " " + nextMotion);
- return null;
- }
- if (target == CONSUMED_EVENT_TOKEN) {
- if (DEBUG_INPUT) Slog.v(TAG, "Consumed: " + nextKey
- + " " + nextMotion);
- return target;
- }
- targetWin = (WindowState)target;
- }
-
- AppWindowToken targetApp = null;
-
- // Now: is it okay to send the next event to this window?
- synchronized (this) {
- // First: did we come here based on the last window not
- // being null, but it changed by the time we got here?
- // If so, try again.
- if (!targetIsNew && mLastWin == null) {
- continue;
- }
-
- // We never dispatch events if not finished with the
- // last one, or the display is frozen.
- if (mFinished && !mDisplayFrozen) {
- // If event dispatching is disabled, then we
- // just consume the events.
- if (!mEventDispatching) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Skipping event; dispatching disabled: "
- + nextKey + " " + nextMotion);
- return null;
- }
- if (targetWin != null) {
- // If this is a new target, and that target is not
- // paused or unresponsive, then all looks good to
- // handle the event.
- if (targetIsNew && !targetWin.mToken.paused) {
- return targetWin;
- }
-
- // If we didn't find a target window, and there is no
- // focused app window, then just eat the events.
- } else if (mFocusedApp == null) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Skipping event; no focused app: "
- + nextKey + " " + nextMotion);
- return null;
- }
- }
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for last key in " + mLastBinder
- + " target=" + targetWin
- + " mFinished=" + mFinished
- + " mDisplayFrozen=" + mDisplayFrozen
- + " targetIsNew=" + targetIsNew
- + " paused="
- + (targetWin != null ? targetWin.mToken.paused : false)
- + " mFocusedApp=" + mFocusedApp
- + " mCurrentFocus=" + mCurrentFocus);
-
- targetApp = targetWin != null
- ? targetWin.mAppToken : mFocusedApp;
-
- long curTimeout = keyDispatchingTimeout;
- if (mTimeToSwitch != 0) {
- long now = SystemClock.uptimeMillis();
- if (mTimeToSwitch <= now) {
- // If an app switch key has been pressed, and we have
- // waited too long for the current app to finish
- // processing keys, then wait no more!
- doFinishedKeyLocked(false);
- continue;
- }
- long switchTimeout = mTimeToSwitch - now;
- if (curTimeout > switchTimeout) {
- curTimeout = switchTimeout;
- }
- }
-
- try {
- // after that continue
- // processing keys, so we don't get stuck.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for key dispatch: " + curTimeout);
- wait(curTimeout);
- if (DEBUG_INPUT) Slog.v(TAG, "Finished waiting @"
- + SystemClock.uptimeMillis() + " startTime="
- + startTime + " switchTime=" + mTimeToSwitch
- + " target=" + targetWin + " mLW=" + mLastWin
- + " mLB=" + mLastBinder + " fin=" + mFinished
- + " mCurrentFocus=" + mCurrentFocus);
- } catch (InterruptedException e) {
- }
- }
-
- // If we were frozen during configuration change, restart the
- // timeout checks from now; otherwise look at whether we timed
- // out before awakening.
- if (mWasFrozen) {
- waitedFor = 0;
- mWasFrozen = false;
- } else {
- waitedFor = SystemClock.uptimeMillis() - startTime;
- }
-
- if (waitedFor >= keyDispatchingTimeout && mTimeToSwitch == 0) {
- IApplicationToken at = null;
- synchronized (this) {
- Slog.w(TAG, "Key dispatching timed out sending to " +
- (targetWin != null ? targetWin.mAttrs.getTitle()
- : "<null>: no window ready for key dispatch"));
- // NOSHIP debugging
- Slog.w(TAG, "Previous dispatch state: " + mDispatchState);
- Slog.w(TAG, "Current dispatch state: " +
- new DispatchState(nextKey, targetWin));
- // END NOSHIP
- //dump();
- if (targetWin != null) {
- at = targetWin.getAppToken();
- } else if (targetApp != null) {
- at = targetApp.appToken;
- }
- }
-
- boolean abort = true;
- if (at != null) {
- try {
- long timeout = at.getKeyDispatchingTimeout();
- if (timeout > waitedFor) {
- // we did not wait the proper amount of time for this application.
- // set the timeout to be the real timeout and wait again.
- keyDispatchingTimeout = timeout - waitedFor;
- continue;
- } else {
- abort = at.keyDispatchingTimedOut();
- }
- } catch (RemoteException ex) {
- }
- }
-
- synchronized (this) {
- if (abort && (mLastWin == targetWin || targetWin == null)) {
- mFinished = true;
- if (mLastWin != null) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "Window " + mLastWin +
- " timed out on key input");
- if (mLastWin.mToken.paused) {
- Slog.w(TAG, "Un-pausing dispatching to this window");
- mLastWin.mToken.paused = false;
- }
- }
- if (mMotionTarget == targetWin) {
- mMotionTarget = null;
- }
- mLastWin = null;
- mLastBinder = null;
- if (failIfTimeout || targetWin == null) {
- return null;
- }
- } else {
- Slog.w(TAG, "Continuing to wait for key to be dispatched");
- startTime = SystemClock.uptimeMillis();
- }
- }
- }
- }
- }
-
- Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
- MotionEvent nextMotion, boolean isPointerEvent,
- int callingPid, int callingUid) {
- mOutsideTouchTargets = null;
-
- if (nextKey != null) {
- // Find the target window for a normal key event.
- final int keycode = nextKey.getKeyCode();
- final int repeatCount = nextKey.getRepeatCount();
- final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
-
- if (!dispatch) {
- if (callingUid == 0 ||
- mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS,
- callingPid, callingUid)
- == PackageManager.PERMISSION_GRANTED) {
- mPolicy.interceptKeyTi(null, keycode,
- nextKey.getMetaState(), down, repeatCount,
- nextKey.getFlags());
- }
- Slog.w(TAG, "Event timeout during app switch: dropping "
- + nextKey);
- return SKIP_TARGET_TOKEN;
- }
-
- // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")");
-
- WindowState focus = null;
- synchronized(mWindowMap) {
- focus = getFocusedWindowLocked();
- }
-
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-
- if (callingUid == 0 ||
- (focus != null && callingUid == focus.mSession.mUid) ||
- mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS,
- callingPid, callingUid)
- == PackageManager.PERMISSION_GRANTED) {
- if (mPolicy.interceptKeyTi(focus,
- keycode, nextKey.getMetaState(), down, repeatCount,
- nextKey.getFlags())) {
- return CONSUMED_EVENT_TOKEN;
- }
- }
-
- return focus;
-
- } else if (!isPointerEvent) {
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1);
- if (!dispatch) {
- Slog.w(TAG, "Event timeout during app switch: dropping trackball "
- + nextMotion);
- return SKIP_TARGET_TOKEN;
- }
-
- WindowState focus = null;
- synchronized(mWindowMap) {
- focus = getFocusedWindowLocked();
- }
-
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
- return focus;
- }
-
- if (nextMotion == null) {
- return SKIP_TARGET_TOKEN;
- }
-
- boolean dispatch = mKeyWaiter.checkShouldDispatchKey(
- KeyEvent.KEYCODE_UNKNOWN);
- if (!dispatch) {
- Slog.w(TAG, "Event timeout during app switch: dropping pointer "
- + nextMotion);
- return SKIP_TARGET_TOKEN;
- }
-
- // Find the target window for a pointer event.
- int action = nextMotion.getAction();
- final float xf = nextMotion.getX();
- final float yf = nextMotion.getY();
- final long eventTime = nextMotion.getEventTime();
-
- final boolean screenWasOff = qev != null
- && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
-
- WindowState target = null;
-
- synchronized(mWindowMap) {
- synchronized (this) {
- if (action == MotionEvent.ACTION_DOWN) {
- if (mMotionTarget != null) {
- // this is weird, we got a pen down, but we thought it was
- // already down!
- // XXX: We should probably send an ACTION_UP to the current
- // target.
- Slog.w(TAG, "Pointer down received while already down in: "
- + mMotionTarget);
- mMotionTarget = null;
- }
-
- // ACTION_DOWN is special, because we need to lock next events to
- // the window we'll land onto.
- final int x = (int)xf;
- final int y = (int)yf;
-
- final ArrayList windows = mWindows;
- final int N = windows.size();
- WindowState topErrWindow = null;
- final Rect tmpRect = mTempRect;
- for (int i=N-1; i>=0; i--) {
- WindowState child = (WindowState)windows.get(i);
- //Slog.i(TAG, "Checking dispatch to: " + child);
- final int flags = child.mAttrs.flags;
- if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
- if (topErrWindow == null) {
- topErrWindow = child;
- }
- }
- if (!child.isVisibleLw()) {
- //Slog.i(TAG, "Not visible!");
- continue;
- }
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- //Slog.i(TAG, "Not touchable!");
- if ((flags & WindowManager.LayoutParams
- .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
- child.mNextOutsideTouch = mOutsideTouchTargets;
- mOutsideTouchTargets = child;
- }
- continue;
- }
- tmpRect.set(child.mFrame);
- if (child.mTouchableInsets == ViewTreeObserver
- .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
- // The touch is inside of the window if it is
- // inside the frame, AND the content part of that
- // frame that was given by the application.
- tmpRect.left += child.mGivenContentInsets.left;
- tmpRect.top += child.mGivenContentInsets.top;
- tmpRect.right -= child.mGivenContentInsets.right;
- tmpRect.bottom -= child.mGivenContentInsets.bottom;
- } else if (child.mTouchableInsets == ViewTreeObserver
- .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
- // The touch is inside of the window if it is
- // inside the frame, AND the visible part of that
- // frame that was given by the application.
- tmpRect.left += child.mGivenVisibleInsets.left;
- tmpRect.top += child.mGivenVisibleInsets.top;
- tmpRect.right -= child.mGivenVisibleInsets.right;
- tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
- }
- final int touchFlags = flags &
- (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
- if (tmpRect.contains(x, y) || touchFlags == 0) {
- //Slog.i(TAG, "Using this target!");
- if (!screenWasOff || (flags &
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
- mMotionTarget = child;
- } else {
- //Slog.i(TAG, "Waking, skip!");
- mMotionTarget = null;
- }
- break;
- }
-
- if ((flags & WindowManager.LayoutParams
- .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
- child.mNextOutsideTouch = mOutsideTouchTargets;
- mOutsideTouchTargets = child;
- //Slog.i(TAG, "Adding to outside target list: " + child);
- }
- }
-
- // if there's an error window but it's not accepting
- // focus (typically because it is not yet visible) just
- // wait for it -- any other focused window may in fact
- // be in ANR state.
- if (topErrWindow != null && mMotionTarget != topErrWindow) {
- mMotionTarget = null;
- }
- }
-
- target = mMotionTarget;
- }
- }
-
- wakeupIfNeeded(target, eventType(nextMotion));
-
- // Pointer events are a little different -- if there isn't a
- // target found for any event, then just drop it.
- return target != null ? target : SKIP_TARGET_TOKEN;
- }
-
- boolean checkShouldDispatchKey(int keycode) {
- synchronized (this) {
- if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) {
- mTimeToSwitch = 0;
- return true;
- }
- if (mTimeToSwitch != 0
- && mTimeToSwitch < SystemClock.uptimeMillis()) {
- return false;
- }
- return true;
- }
- }
-
- void bindTargetWindowLocked(WindowState win,
- int pendingWhat, QueuedEvent pendingMotion) {
- synchronized (this) {
- bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion);
- }
- }
-
- void bindTargetWindowLocked(WindowState win) {
- synchronized (this) {
- bindTargetWindowLockedLocked(win, RETURN_NOTHING, null);
- }
- }
-
- void bindTargetWindowLockedLocked(WindowState win,
- int pendingWhat, QueuedEvent pendingMotion) {
- mLastWin = win;
- mLastBinder = win.mClient.asBinder();
- mFinished = false;
- if (pendingMotion != null) {
- final Session s = win.mSession;
- if (pendingWhat == RETURN_PENDING_POINTER) {
- releasePendingPointerLocked(s);
- s.mPendingPointerMove = pendingMotion;
- s.mPendingPointerWindow = win;
- if (DEBUG_INPUT) Slog.v(TAG,
- "bindTargetToWindow " + s.mPendingPointerMove);
- } else if (pendingWhat == RETURN_PENDING_TRACKBALL) {
- releasePendingTrackballLocked(s);
- s.mPendingTrackballMove = pendingMotion;
- s.mPendingTrackballWindow = win;
- }
- }
- }
-
- void releasePendingPointerLocked(Session s) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "releasePendingPointer " + s.mPendingPointerMove);
- if (s.mPendingPointerMove != null) {
- mQueue.recycleEvent(s.mPendingPointerMove);
- s.mPendingPointerMove = null;
- }
- }
-
- void releasePendingTrackballLocked(Session s) {
- if (s.mPendingTrackballMove != null) {
- mQueue.recycleEvent(s.mPendingTrackballMove);
- s.mPendingTrackballMove = null;
- }
- }
-
- void releaseMotionTarget(WindowState win) {
- if (mMotionTarget == win) {
- mMotionTarget = null;
- }
- }
-
- MotionEvent finishedKey(Session session, IWindow client, boolean force,
- int returnWhat) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: client=" + client + ", force=" + force);
-
- if (client == null) {
- return null;
- }
-
- MotionEvent res = null;
- QueuedEvent qev = null;
- WindowState win = null;
-
- synchronized (this) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: client=" + client.asBinder()
- + ", force=" + force + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")");
-
- if (returnWhat == RETURN_PENDING_POINTER) {
- qev = session.mPendingPointerMove;
- win = session.mPendingPointerWindow;
- session.mPendingPointerMove = null;
- session.mPendingPointerWindow = null;
- } else if (returnWhat == RETURN_PENDING_TRACKBALL) {
- qev = session.mPendingTrackballMove;
- win = session.mPendingTrackballWindow;
- session.mPendingTrackballMove = null;
- session.mPendingTrackballWindow = null;
- }
-
- if (mLastBinder == client.asBinder()) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "finishedKey: last paused="
- + ((mLastWin != null) ? mLastWin.mToken.paused : "null"));
- if (mLastWin != null && (!mLastWin.mToken.paused || force
- || !mEventDispatching)) {
- doFinishedKeyLocked(true);
- } else {
- // Make sure to wake up anyone currently waiting to
- // dispatch a key, so they can re-evaluate their
- // current situation.
- mFinished = true;
- notifyAll();
- }
- }
-
- if (qev != null) {
- res = (MotionEvent)qev.event;
- if (DEBUG_INPUT) Slog.v(TAG,
- "Returning pending motion: " + res);
- mQueue.recycleEvent(qev);
- if (win != null && returnWhat == RETURN_PENDING_POINTER) {
- res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
- }
- }
- }
-
- if (res != null && returnWhat == RETURN_PENDING_POINTER) {
- synchronized (mWindowMap) {
- dispatchPointerElsewhereLocked(win, win, res, res.getEventTime(), false);
- }
- }
-
- return res;
- }
-
- void tickle() {
- synchronized (this) {
- notifyAll();
- }
- }
-
- void handleNewWindowLocked(WindowState newWindow) {
- if (!newWindow.canReceiveKeys()) {
- return;
- }
- synchronized (this) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "New key dispatch window: win="
- + newWindow.mClient.asBinder()
- + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
- + "), finished=" + mFinished + ", paused="
- + newWindow.mToken.paused);
-
- // Displaying a window implicitly causes dispatching to
- // be unpaused. (This is to protect against bugs if someone
- // pauses dispatching but forgets to resume.)
- newWindow.mToken.paused = false;
-
- mGotFirstWindow = true;
-
- if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
- if (DEBUG_INPUT) Slog.v(TAG,
- "New SYSTEM_ERROR window; resetting state");
- mLastWin = null;
- mLastBinder = null;
- mMotionTarget = null;
- mFinished = true;
- } else if (mLastWin != null) {
- // If the new window is above the window we are
- // waiting on, then stop waiting and let key dispatching
- // start on the new guy.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Last win layer=" + mLastWin.mLayer
- + ", new win layer=" + newWindow.mLayer);
- if (newWindow.mLayer >= mLastWin.mLayer) {
- // The new window is above the old; finish pending input to the last
- // window and start directing it to the new one.
- mLastWin.mToken.paused = false;
- doFinishedKeyLocked(false); // does a notifyAll()
- return;
- }
- }
-
- // Now that we've put a new window state in place, make the event waiter
- // take notice and retarget its attentions.
- notifyAll();
- }
- }
-
- void pauseDispatchingLocked(WindowToken token) {
- synchronized (this)
- {
- if (DEBUG_INPUT) Slog.v(TAG, "Pausing WindowToken " + token);
- token.paused = true;
-
- /*
- if (mLastWin != null && !mFinished && mLastWin.mBaseLayer <= layer) {
- mPaused = true;
- } else {
- if (mLastWin == null) {
- Slog.i(TAG, "Key dispatching not paused: no last window.");
- } else if (mFinished) {
- Slog.i(TAG, "Key dispatching not paused: finished last key.");
- } else {
- Slog.i(TAG, "Key dispatching not paused: window in higher layer.");
- }
- }
- */
- }
- }
-
- void resumeDispatchingLocked(WindowToken token) {
- synchronized (this) {
- if (token.paused) {
- if (DEBUG_INPUT) Slog.v(
- TAG, "Resuming WindowToken " + token
- + ", last=" + mLastBinder
- + " (token=" + (mLastWin != null ? mLastWin.mToken : null)
- + "), finished=" + mFinished + ", paused="
- + token.paused);
- token.paused = false;
- if (mLastWin != null && mLastWin.mToken == token && mFinished) {
- doFinishedKeyLocked(false);
- } else {
- notifyAll();
- }
- }
- }
- }
-
- void setEventDispatchingLocked(boolean enabled) {
- synchronized (this) {
- mEventDispatching = enabled;
- notifyAll();
- }
- }
-
- void appSwitchComing() {
- synchronized (this) {
- // Don't wait for more than .5 seconds for app to finish
- // processing the pending events.
- long now = SystemClock.uptimeMillis() + 500;
- if (DEBUG_INPUT) Slog.v(TAG, "appSwitchComing: " + now);
- if (mTimeToSwitch == 0 || now < mTimeToSwitch) {
- mTimeToSwitch = now;
- }
- notifyAll();
- }
- }
-
- private final void doFinishedKeyLocked(boolean force) {
- if (mLastWin != null) {
- releasePendingPointerLocked(mLastWin.mSession);
- releasePendingTrackballLocked(mLastWin.mSession);
- }
-
- if (force || mLastWin == null || !mLastWin.mToken.paused
- || !mLastWin.isVisibleLw()) {
- // If the current window has been paused, we aren't -really-
- // finished... so let the waiters still wait.
- mLastWin = null;
- mLastBinder = null;
- }
- mFinished = true;
- notifyAll();
- }
- }
-
- private class KeyQ extends KeyInputQueue
- implements KeyInputQueue.FilterCallback {
- PowerManager.WakeLock mHoldingScreen;
-
- KeyQ() {
- super(mContext, WindowManagerService.this);
- PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
- "KEEP_SCREEN_ON_FLAG");
- mHoldingScreen.setReferenceCounted(false);
- }
-
- @Override
- boolean preprocessEvent(InputDevice device, RawInputEvent event) {
- if (mPolicy.preprocessInputEventTq(event)) {
- return true;
- }
-
- switch (event.type) {
- case RawInputEvent.EV_KEY: {
- // XXX begin hack
- if (DEBUG) {
- if (event.keycode == KeyEvent.KEYCODE_G) {
- if (event.value != 0) {
- // G down
- mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- }
- return false;
- }
- if (event.keycode == KeyEvent.KEYCODE_D) {
- if (event.value != 0) {
- //dump();
- }
- return false;
- }
- }
- // XXX end hack
-
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- int actions = mPolicy.interceptKeyTq(event, !screenIsOff);
-
- if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {
- mPowerManager.goToSleep(event.when);
- }
-
- if (screenIsOff) {
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {
- mPowerManager.userActivity(event.when, false,
- LocalPowerManager.BUTTON_EVENT, false);
- }
-
- if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
- if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) {
- filterQueue(this);
- mKeyWaiter.appSwitchComing();
- }
- return true;
- } else {
- return false;
- }
- }
-
- case RawInputEvent.EV_REL: {
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- if (screenIsOff) {
- if (!mPolicy.isWakeRelMovementTq(event.deviceId,
- device.classes, event)) {
- //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey");
- return false;
- }
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- return true;
- }
-
- case RawInputEvent.EV_ABS: {
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- if (screenIsOff) {
- if (!mPolicy.isWakeAbsMovementTq(event.deviceId,
- device.classes, event)) {
- //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey");
- return false;
- }
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- return true;
- }
-
- default:
- return true;
- }
- }
-
- public int filterEvent(QueuedEvent ev) {
- switch (ev.classType) {
- case RawInputEvent.CLASS_KEYBOARD:
- KeyEvent ke = (KeyEvent)ev.event;
- if (mPolicy.isMovementKeyTi(ke.getKeyCode())) {
- Slog.w(TAG, "Dropping movement key during app switch: "
- + ke.getKeyCode() + ", action=" + ke.getAction());
- return FILTER_REMOVE;
- }
- return FILTER_ABORT;
- default:
- return FILTER_KEEP;
- }
- }
-
- /**
- * Must be called with the main window manager lock held.
- */
- void setHoldScreenLocked(boolean holding) {
- boolean state = mHoldingScreen.isHeld();
- if (holding != state) {
- if (holding) {
- mHoldingScreen.acquire();
- } else {
- mPolicy.screenOnStoppedLw();
- mHoldingScreen.release();
- }
- }
- }
- }
-
public boolean detectSafeMode() {
mSafeMode = mPolicy.detectSafeMode();
return mSafeMode;
@@ -6475,219 +5618,6 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.systemReady();
}
- private final class InputDispatcherThread extends Thread {
- // Time to wait when there is nothing to do: 9999 seconds.
- static final int LONG_WAIT=9999*1000;
-
- public InputDispatcherThread() {
- super("InputDispatcher");
- }
-
- @Override
- public void run() {
- while (true) {
- try {
- process();
- } catch (Exception e) {
- Slog.e(TAG, "Exception in input dispatcher", e);
- }
- }
- }
-
- private void process() {
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
- // The last key event we saw
- KeyEvent lastKey = null;
-
- // Last keydown time for auto-repeating keys
- long lastKeyTime = SystemClock.uptimeMillis();
- long nextKeyTime = lastKeyTime+LONG_WAIT;
- long downTime = 0;
-
- // How many successive repeats we generated
- int keyRepeatCount = 0;
-
- // Need to report that configuration has changed?
- boolean configChanged = false;
-
- while (true) {
- long curTime = SystemClock.uptimeMillis();
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for next key: now=" + curTime
- + ", repeat @ " + nextKeyTime);
-
- // Retrieve next event, waiting only as long as the next
- // repeat timeout. If the configuration has changed, then
- // don't wait at all -- we'll report the change as soon as
- // we have processed all events.
- QueuedEvent ev = mQueue.getEvent(
- (int)((!configChanged && curTime < nextKeyTime)
- ? (nextKeyTime-curTime) : 0));
-
- if (DEBUG_INPUT && ev != null) Slog.v(
- TAG, "Event: type=" + ev.classType + " data=" + ev.event);
-
- if (MEASURE_LATENCY) {
- lt.sample("2 got event ", System.nanoTime() - ev.whenNano);
- }
-
- if (lastKey != null && !mPolicy.allowKeyRepeat()) {
- // cancel key repeat at the request of the policy.
- lastKey = null;
- downTime = 0;
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- }
- try {
- if (ev != null) {
- curTime = SystemClock.uptimeMillis();
- int eventType;
- if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
- eventType = eventType((MotionEvent)ev.event);
- } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
- ev.classType == RawInputEvent.CLASS_TRACKBALL) {
- eventType = LocalPowerManager.BUTTON_EVENT;
- } else {
- eventType = LocalPowerManager.OTHER_EVENT;
- }
- try {
- if ((curTime - mLastBatteryStatsCallTime)
- >= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastBatteryStatsCallTime = curTime;
- mBatteryStats.noteInputEvent();
- }
- } catch (RemoteException e) {
- // Ignore
- }
-
- if (ev.classType == RawInputEvent.CLASS_CONFIGURATION_CHANGED) {
- // do not wake screen in this case
- } else if (eventType != TOUCH_EVENT
- && eventType != LONG_TOUCH_EVENT
- && eventType != CHEEK_EVENT) {
- mPowerManager.userActivity(curTime, false,
- eventType, false);
- } else if (mLastTouchEventType != eventType
- || (curTime - mLastUserActivityCallTime)
- >= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastUserActivityCallTime = curTime;
- mLastTouchEventType = eventType;
- mPowerManager.userActivity(curTime, false,
- eventType, false);
- }
-
- switch (ev.classType) {
- case RawInputEvent.CLASS_KEYBOARD:
- KeyEvent ke = (KeyEvent)ev.event;
- if (ke.isDown()) {
- lastKeyTime = curTime;
- if (lastKey != null &&
- ke.getKeyCode() == lastKey.getKeyCode()) {
- keyRepeatCount++;
- // Arbitrary long timeout to block
- // repeating here since we know that
- // the device driver takes care of it.
- nextKeyTime = lastKeyTime + LONG_WAIT;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received repeated key down");
- } else {
- downTime = curTime;
- keyRepeatCount = 0;
- nextKeyTime = lastKeyTime
- + ViewConfiguration.getLongPressTimeout();
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received key down: first repeat @ "
- + nextKeyTime);
- }
- lastKey = ke;
- } else {
- lastKey = null;
- downTime = 0;
- keyRepeatCount = 0;
- // Arbitrary long timeout.
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received key up: ignore repeat @ "
- + nextKeyTime);
- }
- if (keyRepeatCount > 0) {
- dispatchKey(KeyEvent.changeTimeRepeat(ke,
- ke.getEventTime(), keyRepeatCount), 0, 0);
- } else {
- dispatchKey(ke, 0, 0);
- }
- mQueue.recycleEvent(ev);
- break;
- case RawInputEvent.CLASS_TOUCHSCREEN:
- //Slog.i(TAG, "Read next event " + ev);
- dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
- break;
- case RawInputEvent.CLASS_TRACKBALL:
- dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
- break;
- case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
- configChanged = true;
- break;
- default:
- mQueue.recycleEvent(ev);
- break;
- }
-
- } else if (configChanged) {
- configChanged = false;
- sendNewConfiguration();
-
- } else if (lastKey != null) {
- curTime = SystemClock.uptimeMillis();
-
- // Timeout occurred while key was down. If it is at or
- // past the key repeat time, dispatch the repeat.
- if (DEBUG_INPUT) Slog.v(
- TAG, "Key timeout: repeat=" + nextKeyTime
- + ", now=" + curTime);
- if (curTime < nextKeyTime) {
- continue;
- }
-
- lastKeyTime = nextKeyTime;
- nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;
- keyRepeatCount++;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Key repeat: count=" + keyRepeatCount
- + ", next @ " + nextKeyTime);
- KeyEvent newEvent;
- if (downTime != 0 && (downTime
- + ViewConfiguration.getLongPressTimeout())
- <= curTime) {
- newEvent = KeyEvent.changeTimeRepeat(lastKey,
- curTime, keyRepeatCount,
- lastKey.getFlags() | KeyEvent.FLAG_LONG_PRESS);
- downTime = 0;
- } else {
- newEvent = KeyEvent.changeTimeRepeat(lastKey,
- curTime, keyRepeatCount);
- }
- dispatchKey(newEvent, 0, 0);
-
- } else {
- curTime = SystemClock.uptimeMillis();
-
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- }
-
- } catch (Exception e) {
- Slog.e(TAG,
- "Input thread received uncaught exception: " + e, e);
- }
- }
- }
- }
-
// -------------------------------------------------------------
// Client Session State
// -------------------------------------------------------------
@@ -6703,20 +5633,6 @@ public class WindowManagerService extends IWindowManager.Stub
int mNumWindow = 0;
boolean mClientDead = false;
- /**
- * Current pointer move event being dispatched to client window... must
- * hold key lock to access.
- */
- QueuedEvent mPendingPointerMove;
- WindowState mPendingPointerWindow;
-
- /**
- * Current trackball move event being dispatched to client window... must
- * hold key lock to access.
- */
- QueuedEvent mPendingTrackballMove;
- WindowState mPendingTrackballWindow;
-
public Session(IInputMethodClient client, IInputContext inputContext) {
mClient = client;
mInputContext = inputContext;
@@ -6792,8 +5708,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
public int add(IWindow window, WindowManager.LayoutParams attrs,
+ int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
+ return addWindow(this, window, attrs, viewVisibility, outContentInsets,
+ outInputChannel);
+ }
+
+ public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
- return addWindow(this, window, attrs, viewVisibility, outContentInsets);
+ return addWindow(this, window, attrs, viewVisibility, outContentInsets, null);
}
public void remove(IWindow window) {
@@ -6829,27 +5751,6 @@ public class WindowManagerService extends IWindowManager.Stub
finishDrawingWindow(this, window);
}
- public void finishKey(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow finishKey called for " + window);
- mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_NOTHING);
- }
-
- public MotionEvent getPendingPointerMove(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow getPendingMotionEvent called for " + window);
- return mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_PENDING_POINTER);
- }
-
- public MotionEvent getPendingTrackballMove(IWindow window) {
- if (localLOGV) Slog.v(
- TAG, "IWindow getPendingMotionEvent called for " + window);
- return mKeyWaiter.finishedKey(this, window, false,
- KeyWaiter.RETURN_PENDING_TRACKBALL);
- }
-
public void setInTouchMode(boolean mode) {
synchronized(mWindowMap) {
mInTouchMode = mode;
@@ -6953,16 +5854,6 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
pw.print(" mClientDead="); pw.print(mClientDead);
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
- if (mPendingPointerWindow != null || mPendingPointerMove != null) {
- pw.print(prefix);
- pw.print("mPendingPointerWindow="); pw.print(mPendingPointerWindow);
- pw.print(" mPendingPointerMove="); pw.println(mPendingPointerMove);
- }
- if (mPendingTrackballWindow != null || mPendingTrackballMove != null) {
- pw.print(prefix);
- pw.print("mPendingTrackballWindow="); pw.print(mPendingTrackballWindow);
- pw.print(" mPendingTrackballMove="); pw.println(mPendingTrackballMove);
- }
}
@Override
@@ -6985,7 +5876,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
final DeathRecipient mDeathRecipient;
final WindowState mAttachedWindow;
- final ArrayList mChildWindows = new ArrayList();
+ final ArrayList<WindowState> mChildWindows = new ArrayList<WindowState>();
final int mBaseLayer;
final int mSubLayer;
final boolean mLayoutAttached;
@@ -7013,8 +5904,6 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mObscured;
boolean mTurnOnScreen;
- WindowState mNextOutsideTouch;
-
int mLayoutSeq = -1;
Configuration mConfiguration = null;
@@ -7162,6 +6051,9 @@ public class WindowManagerService extends IWindowManager.Stub
int mSurfaceLayer;
float mSurfaceAlpha;
+ // Input channel
+ InputChannel mInputChannel;
+
WindowState(Session s, IWindow c, WindowToken token,
WindowState attachedWindow, WindowManager.LayoutParams a,
int viewVisibility) {
@@ -7389,6 +6281,12 @@ public class WindowManagerService extends IWindowManager.Stub
public IApplicationToken getAppToken() {
return mAppToken != null ? mAppToken.appToken : null;
}
+
+ public long getInputDispatchingTimeoutNanos() {
+ return mAppToken != null
+ ? mAppToken.inputDispatchingTimeoutNanos
+ : DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
public boolean hasAppShownWindows() {
return mAppToken != null ? mAppToken.firstWindowDrawn : false;
@@ -7522,14 +6420,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
void destroySurfaceLocked() {
- // Window is no longer on-screen, so can no longer receive
- // key events... if we were waiting for it to finish
- // handling a key event, the wait is over!
- mKeyWaiter.finishedKey(mSession, mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releasePendingPointerLocked(mSession);
- mKeyWaiter.releasePendingTrackballLocked(mSession);
-
if (mAppToken != null && this == mAppToken.startingWindow) {
mAppToken.startingDisplayed = false;
}
@@ -7542,7 +6432,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = mChildWindows.size();
while (i > 0) {
i--;
- WindowState c = (WindowState)mChildWindows.get(i);
+ WindowState c = mChildWindows.get(i);
c.mAttachedHidden = true;
}
@@ -7653,7 +6543,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = mChildWindows.size();
while (i > 0) {
i--;
- WindowState c = (WindowState)mChildWindows.get(i);
+ WindowState c = mChildWindows.get(i);
if (c.mAttachedHidden) {
c.mAttachedHidden = false;
if (c.mSurface != null) {
@@ -7826,7 +6716,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = mChildWindows.size();
for (int i=0; i<N; i++) {
- ((WindowState)mChildWindows.get(i)).finishExit();
+ mChildWindows.get(i).finishExit();
}
if (!mExiting) {
@@ -7851,7 +6741,6 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Error hiding surface in " + this, e);
}
mLastHidden = true;
- mKeyWaiter.releasePendingPointerLocked(mSession);
}
mExiting = false;
if (mRemoveOnExit) {
@@ -8175,6 +7064,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
void removeLocked() {
+ disposeInputChannel();
+
if (mAttachedWindow != null) {
mAttachedWindow.mChildWindows.remove(this);
}
@@ -8187,6 +7078,15 @@ public class WindowManagerService extends IWindowManager.Stub
// we are doing this as part of processing a death note.)
}
}
+
+ void disposeInputChannel() {
+ if (mInputChannel != null) {
+ mInputManager.unregisterInputChannel(mInputChannel);
+
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
private class DeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
@@ -8428,6 +7328,11 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
}
}
+
+ String makeInputChannelName() {
+ return Integer.toHexString(System.identityHashCode(this))
+ + " " + mAttrs.getTitle();
+ }
@Override
public String toString() {
@@ -8530,6 +7435,9 @@ public class WindowManagerService extends IWindowManager.Stub
int groupId = -1;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ // The input dispatching timeout for this application token in nanoseconds.
+ long inputDispatchingTimeoutNanos;
// These are used for determining when all windows associated with
// an activity have been drawn, so they can be made visible together
@@ -8737,7 +7645,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = windows.size();
for (int i=0; i<N; i++) {
- ((WindowState)windows.get(i)).finishExit();
+ windows.get(i).finishExit();
}
updateReportedVisibilityLocked();
@@ -8936,6 +7844,7 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int ENABLE_SCREEN = 16;
public static final int APP_FREEZE_TIMEOUT = 17;
public static final int SEND_NEW_CONFIGURATION = 18;
+ public static final int REPORT_WINDOWS_CHANGE = 19;
private Session mLastReportedHold;
@@ -8977,6 +7886,7 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (RemoteException e) {
// Ignore if process has died.
}
+ notifyFocusChanged();
}
if (lastFocus != null) {
@@ -9161,7 +8071,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i = mWindows.size();
while (i > 0) {
i--;
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mOrientationChanging) {
w.mOrientationChanging = false;
Slog.w(TAG, "Force clearing orientation change: " + w);
@@ -9184,12 +8094,12 @@ public class WindowManagerService extends IWindowManager.Stub
if (oldHold != newHold) {
try {
if (oldHold != null) {
- mBatteryStats.noteStopWakelock(oldHold.mUid,
+ mBatteryStats.noteStopWakelock(oldHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
if (newHold != null) {
- mBatteryStats.noteStartWakelock(newHold.mUid,
+ mBatteryStats.noteStartWakelock(newHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
@@ -9267,6 +8177,16 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
+ case REPORT_WINDOWS_CHANGE: {
+ if (mWindowsChanged) {
+ synchronized (mWindowMap) {
+ mWindowsChanged = false;
+ }
+ notifyWindowsChanged();
+ }
+ break;
+ }
+
}
}
}
@@ -9279,7 +8199,8 @@ public class WindowManagerService extends IWindowManager.Stub
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
- return new Session(client, inputContext);
+ Session session = new Session(client, inputContext);
+ return session;
}
public boolean inputMethodClientHasFocus(IInputMethodClient client) {
@@ -9289,7 +8210,7 @@ public class WindowManagerService extends IWindowManager.Stub
int idx = findDesiredInputMethodWindowIndexLocked(false);
WindowState imFocus;
if (idx > 0) {
- imFocus = (WindowState)mWindows.get(idx-1);
+ imFocus = mWindows.get(idx-1);
if (imFocus != null) {
if (imFocus.mSession.mClient != null &&
imFocus.mSession.mClient.asBinder() == client.asBinder()) {
@@ -9347,9 +8268,10 @@ public class WindowManagerService extends IWindowManager.Stub
// First remove all existing app windows.
i=0;
while (i < NW) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mAppToken != null) {
- WindowState win = (WindowState)mWindows.remove(i);
+ WindowState win = mWindows.remove(i);
+ mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Rebuild removing window: " + win);
NW--;
@@ -9396,7 +8318,7 @@ public class WindowManagerService extends IWindowManager.Stub
int i;
for (i=0; i<N; i++) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
|| (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
@@ -9485,6 +8407,10 @@ public class WindowManagerService extends IWindowManager.Stub
requestAnimationLocked(0);
}
}
+ if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
+ mH.removeMessages(H.REPORT_WINDOWS_CHANGE);
+ mH.sendMessage(mH.obtainMessage(H.REPORT_WINDOWS_CHANGE));
+ }
} catch (RuntimeException e) {
mInLayout = false;
Slog.e(TAG, "Unhandled exception while layout out windows", e);
@@ -9517,7 +8443,7 @@ public class WindowManagerService extends IWindowManager.Stub
// to another window).
int topAttached = -1;
for (i = N-1; i >= 0; i--) {
- WindowState win = (WindowState) mWindows.get(i);
+ WindowState win = mWindows.get(i);
// Don't do layout of a window if it is not visible, or
// soon won't be visible, to avoid wasting time and funky
@@ -9566,7 +8492,7 @@ public class WindowManagerService extends IWindowManager.Stub
// XXX does not deal with windows that are attached to windows
// that are themselves attached.
for (i = topAttached; i >= 0; i--) {
- WindowState win = (WindowState) mWindows.get(i);
+ WindowState win = mWindows.get(i);
// If this view is GONE, then skip it -- keep the current
// frame, and let the caller know so they can ignore it
@@ -9589,6 +8515,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
+
+ // Window frames may have changed. Tell the input dispatcher about it.
+ mInputMonitor.updateInputWindowsLw();
return mPolicy.finishLayoutLw();
}
@@ -9606,12 +8535,6 @@ public class WindowManagerService extends IWindowManager.Stub
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
}
- if (mFxSession == null) {
- mFxSession = new SurfaceSession();
- }
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
-
// Initialize state of exiting tokens.
for (i=mExitingTokens.size()-1; i>=0; i--) {
mExitingTokens.get(i).hasVisible = false;
@@ -9628,8 +8551,24 @@ public class WindowManagerService extends IWindowManager.Stub
float buttonBrightness = -1;
boolean focusDisplayed = false;
boolean animating = false;
+ boolean createWatermark = false;
+
+ if (mFxSession == null) {
+ mFxSession = new SurfaceSession();
+ createWatermark = true;
+ }
+
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
Surface.openTransaction();
+
+ if (createWatermark) {
+ createWatermark();
+ }
+ if (mWatermark != null) {
+ mWatermark.positionSurface(dw, dh);
+ }
+
try {
boolean wallpaperForceHidingChanged = false;
int repeats = 0;
@@ -9710,7 +8649,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = mWindows.size();
for (i=N-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -10143,7 +9082,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Clear them out.
forceHiding = false;
for (i=N-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
if (w.mSurface != null) {
final WindowManager.LayoutParams attrs = w.mAttrs;
if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) {
@@ -10193,6 +9132,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
+ Integer.toHexString(changes));
+ mInputMonitor.updateInputWindowsLw();
} while (changes != 0);
// THIRD LOOP: Update the surfaces of all windows.
@@ -10209,7 +9149,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = mWindows.size();
for (i=N-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
boolean displayed = false;
final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -10389,7 +9329,6 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Exception hiding surface in " + w);
}
}
- mKeyWaiter.releasePendingPointerLocked(w.mSession);
}
// If we are waiting for this window to handle an
// orientation change, well, it is hidden, so
@@ -10649,8 +9588,14 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.e(TAG, "Unhandled exception in Window Manager", e);
}
+ mInputMonitor.updateInputWindowsLw();
+
Surface.closeTransaction();
+ if (mWatermark != null) {
+ mWatermark.drawIfNeeded();
+ }
+
if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG,
"With display frozen, orientationChangeComplete="
+ orientationChangeComplete);
@@ -10774,10 +9719,12 @@ public class WindowManagerService extends IWindowManager.Stub
requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
}
+ mInputMonitor.updateInputWindowsLw();
+
if (DEBUG_FREEZE) Slog.v(TAG, "Layout: mDisplayFrozen=" + mDisplayFrozen
+ " holdScreen=" + holdScreen);
if (!mDisplayFrozen) {
- mQueue.setHoldScreenLocked(holdScreen != null);
+ setHoldScreenLocked(holdScreen != null);
if (screenBrightness < 0 || screenBrightness > 1.0f) {
mPowerManager.setScreenBrightnessOverride(-1);
} else {
@@ -10808,6 +9755,21 @@ public class WindowManagerService extends IWindowManager.Stub
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
}
+
+ /**
+ * Must be called with the main window manager lock held.
+ */
+ void setHoldScreenLocked(boolean holding) {
+ boolean state = mHoldingScreenWakeLock.isHeld();
+ if (holding != state) {
+ if (holding) {
+ mHoldingScreenWakeLock.acquire();
+ } else {
+ mPolicy.screenOnStoppedLw();
+ mHoldingScreenWakeLock.release();
+ }
+ }
+ }
void requestAnimationLocked(long delay) {
if (!mAnimationPending) {
@@ -10865,7 +9827,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean leakedSurface = false;
Slog.i(TAG, "Out of memory for surface! Looking for leaks...");
for (int i=0; i<N; i++) {
- WindowState ws = (WindowState)mWindows.get(i);
+ WindowState ws = mWindows.get(i);
if (ws.mSurface != null) {
if (!mSessions.contains(ws.mSession)) {
Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): "
@@ -10897,7 +9859,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "No leaked surfaces; killing applicatons!");
SparseIntArray pidCandidates = new SparseIntArray();
for (int i=0; i<N; i++) {
- WindowState ws = (WindowState)mWindows.get(i);
+ WindowState ws = mWindows.get(i);
if (ws.mSurface != null) {
pidCandidates.append(ws.mSession.mPid, ws.mSession.mPid);
}
@@ -10964,14 +9926,20 @@ public class WindowManagerService extends IWindowManager.Stub
assignLayersLocked();
}
}
-
- if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
- mKeyWaiter.handleNewWindowLocked(newFocus);
+
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
+ // If we defer assigning layers, then the caller is responsible for
+ // doing this part.
+ finishUpdateFocusedWindowAfterAssignLayersLocked();
}
return true;
}
return false;
}
+
+ private void finishUpdateFocusedWindowAfterAssignLayersLocked() {
+ mInputMonitor.setInputFocusLw(mCurrentFocus);
+ }
private WindowState computeFocusedWindowLocked() {
WindowState result = null;
@@ -10983,7 +9951,7 @@ public class WindowManagerService extends IWindowManager.Stub
? mAppTokens.get(nextAppIndex) : null;
while (i >= 0) {
- win = (WindowState)mWindows.get(i);
+ win = mWindows.get(i);
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG, "Looking for focus: " + i
@@ -11044,15 +10012,6 @@ public class WindowManagerService extends IWindowManager.Stub
private void startFreezingDisplayLocked() {
if (mDisplayFrozen) {
- // Freezing the display also suspends key event delivery, to
- // keep events from going astray while the display is reconfigured.
- // If someone has changed orientation again while the screen is
- // still frozen, the events will continue to be blocked while the
- // successive orientation change is processed. To prevent spurious
- // ANRs, we reset the event dispatch timeout in this case.
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
- }
return;
}
@@ -11074,6 +10033,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException());
mDisplayFrozen = true;
+
+ mInputMonitor.freezeInputDispatchingLw();
+
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
mNextAppTransitionPackage = null;
@@ -11105,12 +10067,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
Surface.unfreezeDisplay(0);
- // Reset the key delivery timeout on unfreeze, too. We force a wakeup here
- // too because regular key delivery processing should resume immediately.
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
- mKeyWaiter.notifyAll();
- }
+ mInputMonitor.thawInputDispatchingLw();
// While the display is frozen we don't re-compute the orientation
// to avoid inconsistent states. However, something interesting
@@ -11132,6 +10089,194 @@ public class WindowManagerService extends IWindowManager.Stub
mScreenFrozenLock.release();
}
+ static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps,
+ DisplayMetrics dm) {
+ if (index < tokens.length) {
+ String str = tokens[index];
+ if (str != null && str.length() > 0) {
+ try {
+ int val = Integer.parseInt(str);
+ return val;
+ } catch (Exception e) {
+ }
+ }
+ }
+ if (defUnits == TypedValue.COMPLEX_UNIT_PX) {
+ return defDps;
+ }
+ int val = (int)TypedValue.applyDimension(defUnits, defDps, dm);
+ return val;
+ }
+
+ class Watermark {
+ final String[] mTokens;
+ final String mText;
+ final Paint mTextPaint;
+ final int mTextWidth;
+ final int mTextHeight;
+ final int mTextAscent;
+ final int mTextDescent;
+ final int mDeltaX;
+ final int mDeltaY;
+
+ Surface mSurface;
+ int mLastDW;
+ int mLastDH;
+ boolean mDrawNeeded;
+
+ Watermark(SurfaceSession session, String[] tokens) {
+ final DisplayMetrics dm = new DisplayMetrics();
+ mDisplay.getMetrics(dm);
+
+ if (false) {
+ Log.i(TAG, "*********************** WATERMARK");
+ for (int i=0; i<tokens.length; i++) {
+ Log.i(TAG, " TOKEN #" + i + ": " + tokens[i]);
+ }
+ }
+
+ mTokens = tokens;
+
+ StringBuilder builder = new StringBuilder(32);
+ int len = mTokens[0].length();
+ len = len & ~1;
+ for (int i=0; i<len; i+=2) {
+ int c1 = mTokens[0].charAt(i);
+ int c2 = mTokens[0].charAt(i+1);
+ if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10;
+ else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10;
+ else c1 -= '0';
+ if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10;
+ else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10;
+ else c2 -= '0';
+ builder.append((char)(255-((c1*16)+c2)));
+ }
+ mText = builder.toString();
+ if (false) {
+ Log.i(TAG, "Final text: " + mText);
+ }
+
+ int fontSize = getPropertyInt(tokens, 1,
+ TypedValue.COMPLEX_UNIT_DIP, 20, dm);
+
+ mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.setTextSize(fontSize);
+ mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+
+ FontMetricsInt fm = mTextPaint.getFontMetricsInt();
+ mTextWidth = (int)mTextPaint.measureText(mText);
+ mTextAscent = fm.ascent;
+ mTextDescent = fm.descent;
+ mTextHeight = fm.descent - fm.ascent;
+
+ mDeltaX = getPropertyInt(tokens, 2,
+ TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm);
+ mDeltaY = getPropertyInt(tokens, 3,
+ TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm);
+ int shadowColor = getPropertyInt(tokens, 4,
+ TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm);
+ int color = getPropertyInt(tokens, 5,
+ TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm);
+ int shadowRadius = getPropertyInt(tokens, 6,
+ TypedValue.COMPLEX_UNIT_PX, 7, dm);
+ int shadowDx = getPropertyInt(tokens, 8,
+ TypedValue.COMPLEX_UNIT_PX, 0, dm);
+ int shadowDy = getPropertyInt(tokens, 9,
+ TypedValue.COMPLEX_UNIT_PX, 0, dm);
+
+ mTextPaint.setColor(color);
+ mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
+
+ try {
+ mSurface = new Surface(session, 0,
+ "WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
+ mSurface.setLayer(TYPE_LAYER_MULTIPLIER*100);
+ mSurface.setPosition(0, 0);
+ mSurface.show();
+ } catch (OutOfResourcesException e) {
+ }
+ }
+
+ void positionSurface(int dw, int dh) {
+ if (mLastDW != dw || mLastDH != dh) {
+ mLastDW = dw;
+ mLastDH = dh;
+ mSurface.setSize(dw, dh);
+ mDrawNeeded = true;
+ }
+ }
+
+ void drawIfNeeded() {
+ if (mDrawNeeded) {
+ final int dw = mLastDW;
+ final int dh = mLastDH;
+
+ mDrawNeeded = false;
+ Rect dirty = new Rect(0, 0, dw, dh);
+ Canvas c = null;
+ try {
+ c = mSurface.lockCanvas(dirty);
+ } catch (IllegalArgumentException e) {
+ } catch (OutOfResourcesException e) {
+ }
+ if (c != null) {
+ int deltaX = mDeltaX;
+ int deltaY = mDeltaY;
+
+ // deltaX shouldn't be close to a round fraction of our
+ // x step, or else things will line up too much.
+ int div = (dw+mTextWidth)/deltaX;
+ int rem = (dw+mTextWidth) - (div*deltaX);
+ int qdelta = deltaX/4;
+ if (rem < qdelta || rem > (deltaX-qdelta)) {
+ deltaX += deltaX/3;
+ }
+
+ int y = -mTextHeight;
+ int x = -mTextWidth;
+ while (y < (dh+mTextHeight)) {
+ c.drawText(mText, x, y, mTextPaint);
+ x += deltaX;
+ if (x >= dw) {
+ x -= (dw+mTextWidth);
+ y += deltaY;
+ }
+ }
+ mSurface.unlockCanvasAndPost(c);
+ }
+ }
+ }
+ }
+
+ void createWatermark() {
+ if (mWatermark != null) {
+ return;
+ }
+
+ File file = new File("/system/etc/setup.conf");
+ FileInputStream in = null;
+ try {
+ in = new FileInputStream(file);
+ DataInputStream ind = new DataInputStream(in);
+ String line = ind.readLine();
+ if (line != null) {
+ String[] toks = line.split("%");
+ if (toks != null && toks.length > 0) {
+ mWatermark = new Watermark(mFxSession, toks);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
@@ -11142,14 +10287,13 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- pw.println("Input State:");
- mQueue.dump(pw, " ");
+ mInputManager.dump(pw);
pw.println(" ");
synchronized(mWindowMap) {
pw.println("Current Window Manager state:");
for (int i=mWindows.size()-1; i>=0; i--) {
- WindowState w = (WindowState)mWindows.get(i);
+ WindowState w = mWindows.get(i);
pw.print(" Window #"); pw.print(i); pw.print(' ');
pw.print(w); pw.println(":");
w.dump(pw, " ");
@@ -11360,24 +10504,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
- pw.println(" KeyWaiter state:");
- pw.print(" mLastWin="); pw.print(mKeyWaiter.mLastWin);
- pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
- pw.print(" mFinished="); pw.print(mKeyWaiter.mFinished);
- pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
- pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
- pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
}
}
+ // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection).
public void monitor() {
synchronized (mWindowMap) { }
synchronized (mKeyguardTokenWatcher) { }
- synchronized (mKeyWaiter) { }
- }
-
- public void virtualKeyFeedback(KeyEvent event) {
- mPolicy.keyFeedbackFromInput(event);
}
/**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 804af9c..7c1a9b0 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import com.android.internal.R;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -24,6 +25,7 @@ import com.android.server.ProcessStats;
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.WindowManagerService;
+import com.android.server.am.ActivityStack.ActivityState;
import dalvik.system.Zygote;
@@ -32,19 +34,20 @@ import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.AlertDialog;
+import android.app.AppGlobals;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IActivityController;
-import android.app.IActivityManager;
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
+import android.app.INotificationManager;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ResultInfo;
import android.app.Service;
import android.app.backup.IBackupManager;
import android.content.ActivityNotFoundException;
@@ -70,6 +73,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.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -88,11 +92,11 @@ import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -102,6 +106,7 @@ import android.util.Slog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -177,47 +182,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Maximum number of recent tasks that we can remember.
static final int MAX_RECENT_TASKS = 20;
-
+
// Amount of time after a call to stopAppSwitches() during which we will
// prevent further untrusted switches from happening.
static final long APP_SWITCH_DELAY_TIME = 5*1000;
-
- // How long until we reset a task when the user returns to it. Currently
- // 30 minutes.
- static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
-
- // Set to true to disable the icon that is shown while a new activity
- // is being started.
- static final boolean SHOW_APP_STARTING_ICON = true;
-
- // How long we wait until giving up on the last activity to pause. This
- // is short because it directly impacts the responsiveness of starting the
- // next activity.
- static final int PAUSE_TIMEOUT = 500;
-
- /**
- * How long we can hold the launch wake lock before giving up.
- */
- static final int LAUNCH_TIMEOUT = 10*1000;
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
- // How long we wait until giving up on the last activity telling us it
- // is idle.
- static final int IDLE_TIMEOUT = 10*1000;
-
// How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000;
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
- // How long we wait until giving up on an activity telling us it has
- // finished destroying itself.
- static final int DESTROY_TIMEOUT = 10*1000;
-
+ // The rate at which we check for apps using excessive wake locks -- 15 mins.
+ static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000;
+
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_TIMEOUT = 10*1000;
@@ -284,6 +266,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// system/rootdir/init.rc on startup.
static final int SECONDARY_SERVER_ADJ;
+ // This is a process with a heavy-weight application. It is in the
+ // background, but we want to try to avoid killing it. Value set in
+ // system/rootdir/init.rc on startup.
+ static final int HEAVY_WEIGHT_APP_ADJ;
+
+ // This is a process only hosting components that are perceptible to the
+ // user, and we really want to avoid killing them, but they are not
+ // immediately visible. An example is background music playback. Value set in
+ // system/rootdir/init.rc on startup.
+ static final int PERCEPTIBLE_APP_ADJ;
+
// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear. Value set in
// system/rootdir/init.rc on startup.
@@ -309,6 +302,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final int HOME_APP_MEM;
static final int BACKUP_APP_MEM;
static final int SECONDARY_SERVER_MEM;
+ static final int HEAVY_WEIGHT_APP_MEM;
+ static final int PERCEPTIBLE_APP_MEM;
static final int VISIBLE_APP_MEM;
static final int FOREGROUND_APP_MEM;
@@ -329,68 +324,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// been idle for less than 120 seconds.
static final long EMPTY_APP_IDLE_OFFSET = 120*1000;
+ static int getIntProp(String name, boolean allowZero) {
+ String str = SystemProperties.get(name);
+ if (str == null) {
+ throw new IllegalArgumentException("Property not defined: " + name);
+ }
+ int val = Integer.valueOf(str);
+ if (val == 0 && !allowZero) {
+ throw new IllegalArgumentException("Property must not be zero: " + name);
+ }
+ return val;
+ }
+
static {
// These values are set in system/rootdir/init.rc on startup.
- FOREGROUND_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_ADJ"));
- VISIBLE_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ"));
- SECONDARY_SERVER_ADJ =
- Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ"));
- BACKUP_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ"));
- HOME_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ"));
- HIDDEN_APP_MIN_ADJ =
- Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MIN_ADJ"));
- EMPTY_APP_ADJ =
- Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_ADJ"));
- HIDDEN_APP_MAX_ADJ = EMPTY_APP_ADJ-1;
- FOREGROUND_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_MEM"))*PAGE_SIZE;
- VISIBLE_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE;
- SECONDARY_SERVER_MEM =
- Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
- BACKUP_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE;
- HOME_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE;
- HIDDEN_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MEM"))*PAGE_SIZE;
- EMPTY_APP_MEM =
- Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_MEM"))*PAGE_SIZE;
+ FOREGROUND_APP_ADJ = getIntProp("ro.FOREGROUND_APP_ADJ", true);
+ VISIBLE_APP_ADJ = getIntProp("ro.VISIBLE_APP_ADJ", true);
+ PERCEPTIBLE_APP_ADJ = getIntProp("ro.PERCEPTIBLE_APP_ADJ", true);
+ HEAVY_WEIGHT_APP_ADJ = getIntProp("ro.HEAVY_WEIGHT_APP_ADJ", true);
+ SECONDARY_SERVER_ADJ = getIntProp("ro.SECONDARY_SERVER_ADJ", true);
+ BACKUP_APP_ADJ = getIntProp("ro.BACKUP_APP_ADJ", true);
+ HOME_APP_ADJ = getIntProp("ro.HOME_APP_ADJ", true);
+ HIDDEN_APP_MIN_ADJ = getIntProp("ro.HIDDEN_APP_MIN_ADJ", true);
+ EMPTY_APP_ADJ = getIntProp("ro.EMPTY_APP_ADJ", true);
+ // These days we use the last empty slot for hidden apps as well.
+ HIDDEN_APP_MAX_ADJ = EMPTY_APP_ADJ;
+ FOREGROUND_APP_MEM = getIntProp("ro.FOREGROUND_APP_MEM", false)*PAGE_SIZE;
+ VISIBLE_APP_MEM = getIntProp("ro.VISIBLE_APP_MEM", false)*PAGE_SIZE;
+ PERCEPTIBLE_APP_MEM = getIntProp("ro.PERCEPTIBLE_APP_MEM", false)*PAGE_SIZE;
+ HEAVY_WEIGHT_APP_MEM = getIntProp("ro.HEAVY_WEIGHT_APP_MEM", false)*PAGE_SIZE;
+ SECONDARY_SERVER_MEM = getIntProp("ro.SECONDARY_SERVER_MEM", false)*PAGE_SIZE;
+ BACKUP_APP_MEM = getIntProp("ro.BACKUP_APP_MEM", false)*PAGE_SIZE;
+ HOME_APP_MEM = getIntProp("ro.HOME_APP_MEM", false)*PAGE_SIZE;
+ HIDDEN_APP_MEM = getIntProp("ro.HIDDEN_APP_MEM", false)*PAGE_SIZE;
+ EMPTY_APP_MEM = getIntProp("ro.EMPTY_APP_MEM", false)*PAGE_SIZE;
}
static final int MY_PID = Process.myPid();
static final String[] EMPTY_STRING_ARRAY = new String[0];
- enum ActivityState {
- INITIALIZING,
- RESUMED,
- PAUSING,
- PAUSED,
- STOPPING,
- STOPPED,
- FINISHING,
- DESTROYING,
- DESTROYED
- }
-
- /**
- * The back history of all previous (and possibly still
- * running) activities. It contains HistoryRecord objects.
- */
- final ArrayList mHistory = new ArrayList();
-
+ public ActivityStack mMainStack;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
*/
- class PendingActivityLaunch {
- HistoryRecord r;
- HistoryRecord sourceRecord;
+ static class PendingActivityLaunch {
+ ActivityRecord r;
+ ActivityRecord sourceRecord;
Uri[] grantedUriPermissions;
int grantedMode;
boolean onlyIfNeeded;
@@ -400,18 +382,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new ArrayList<PendingActivityLaunch>();
/**
- * List of people waiting to find out about the next launched activity.
- */
- final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched
- = new ArrayList<IActivityManager.WaitResult>();
-
- /**
- * List of people waiting to find out about the next visible activity.
- */
- final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
- = new ArrayList<IActivityManager.WaitResult>();
-
- /**
* 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
@@ -441,56 +411,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean mBroadcastsScheduled = false;
/**
- * Set to indicate whether to issue an onUserLeaving callback when a
- * newly launched activity is being brought in front of us.
- */
- boolean mUserLeaving = false;
-
- /**
- * When we are in the process of pausing an activity, before starting the
- * next one, this variable holds the activity that is currently being paused.
- */
- HistoryRecord mPausingActivity = null;
-
- /**
- * Current activity that is resumed, or null if there is none.
- */
- HistoryRecord mResumedActivity = null;
-
- /**
* Activity we have told the window manager to have key focus.
*/
- HistoryRecord mFocusedActivity = null;
-
- /**
- * This is the last activity that we put into the paused state. This is
- * used to determine if we need to do an activity transition while sleeping,
- * when we normally hold the top activity paused.
- */
- HistoryRecord mLastPausedActivity = null;
-
- /**
- * List of activities that are waiting for a new activity
- * to become visible before completing whatever operation they are
- * supposed to do.
- */
- final ArrayList mWaitingVisibleActivities = new ArrayList();
-
- /**
- * List of activities that are ready to be stopped, but waiting
- * for the next activity to settle down before doing so. It contains
- * HistoryRecord objects.
- */
- final ArrayList<HistoryRecord> mStoppingActivities
- = new ArrayList<HistoryRecord>();
-
- /**
- * Animations that for the current transition have requested not to
- * be considered for the transition animation.
- */
- final ArrayList<HistoryRecord> mNoAnimActivities
- = new ArrayList<HistoryRecord>();
-
+ ActivityRecord mFocusedActivity = null;
/**
* List of intents that were used to start the most recent tasks.
*/
@@ -498,13 +421,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new ArrayList<TaskRecord>();
/**
- * List of activities that are ready to be finished, but waiting
- * for the previous activity to settle down before doing so. It contains
- * HistoryRecord objects.
- */
- final ArrayList mFinishingActivities = new ArrayList();
-
- /**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
* returned by the package manager), and the keys are ApplicationRecord
@@ -514,6 +430,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new ProcessMap<ProcessRecord>();
/**
+ * The currently running heavy-weight process, if any.
+ */
+ ProcessRecord mHeavyWeightProcess = null;
+
+ /**
* The last time that various processes have crashed.
*/
final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
@@ -599,16 +520,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* This is the process holding what we currently consider to be
* the "home" activity.
*/
- private ProcessRecord mHomeProcess;
+ ProcessRecord mHomeProcess;
/**
- * List of running activities, sorted by recent usage.
- * The first entry in the list is the least recently used.
- * It contains HistoryRecord objects.
- */
- private final ArrayList mLRUActivities = new ArrayList();
-
- /**
* Set of PendingResultRecord objects that are currently active.
*/
final HashSet mPendingResultRecords = new HashSet();
@@ -620,6 +534,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
= new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
/**
+ * Fingerprints (String.hashCode()) of stack traces that we've
+ * already logged DropBox entries for. Guarded by itself. If
+ * something (rogue user app) forces this over
+ * MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
+ */
+ private final HashSet<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>();
+ private static final int MAX_DUP_SUPPRESSED_STACKS = 5000;
+
+ /**
+ * Strict Mode background batched logging state.
+ *
+ * The string buffer is guarded by itself, and its lock is also
+ * used to determine if another batched write is already
+ * in-flight.
+ */
+ private final StringBuilder mStrictModeBuffer = new StringBuilder();
+
+ /**
* 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
@@ -678,8 +610,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* All currently bound service connections. Keys are the IBinder of
* the client's IServiceConnection.
*/
- final HashMap<IBinder, ConnectionRecord> mServiceConnections
- = new HashMap<IBinder, ConnectionRecord>();
+ final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections
+ = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
/**
* List of services that we have been asked to start,
@@ -727,21 +659,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* that a single provider may be published under multiple names, so
* there may be multiple entries here for a single one in mProvidersByClass.
*/
- final HashMap mProvidersByName = new HashMap();
+ final HashMap<String, ContentProviderRecord> mProvidersByName
+ = new HashMap<String, ContentProviderRecord>();
/**
* All of the currently running global content providers. Keys are a
* string containing the provider's implementation class and values are a
* ContentProviderRecord object containing the data about it.
*/
- final HashMap mProvidersByClass = new HashMap();
+ final HashMap<String, ContentProviderRecord> mProvidersByClass
+ = new HashMap<String, ContentProviderRecord>();
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
* removed from this list once it is published.
*/
- final ArrayList mLaunchingProviders = new ArrayList();
+ final ArrayList<ContentProviderRecord> mLaunchingProviders
+ = new ArrayList<ContentProviderRecord>();
/**
* Global set of specific Uri permissions that have been granted.
@@ -790,12 +725,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int mConfigurationSeq = 0;
/**
- * Set when we know we are going to be calling updateConfiguration()
- * soon, so want to skip intermediate config checks.
- */
- boolean mConfigWillChange;
-
- /**
* Hardware-reported OpenGLES version.
*/
final int GL_ES_VERSION;
@@ -842,6 +771,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean mDidAppSwitch;
/**
+ * Last time (in realtime) at which we checked for wake lock usage.
+ */
+ long mLastWakeLockCheckTime;
+
+ /**
* Set while we are wanting to sleep, to prevent any
* activities from being started/resumed.
*/
@@ -851,21 +785,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* Set if we are shutting down the system, similar to sleeping.
*/
boolean mShuttingDown = false;
-
- /**
- * Set when the system is going to sleep, until we have
- * successfully paused the current activity and released our wake lock.
- * At that point the system is allowed to actually sleep.
- */
- PowerManager.WakeLock mGoingToSleep;
-
- /**
- * We don't want to allow the device to go to sleep while in the process
- * of launching an activity. This is primarily to allow alarm intent
- * receivers to launch an activity and get that to run before the device
- * goes back to sleep.
- */
- PowerManager.WakeLock mLaunchingActivity;
/**
* Task identifier that activities are currently being started
@@ -945,8 +864,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
long mLastWriteTime = 0;
- long mInitialStartTime = 0;
-
/**
* Set to true after the system has finished booting.
*/
@@ -978,7 +895,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (localLOGV) Slog.v(
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
- removeRequestedPss(mApp);
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread);
}
@@ -993,20 +909,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final int WAIT_FOR_DEBUGGER_MSG = 6;
static final int BROADCAST_INTENT_MSG = 7;
static final int BROADCAST_TIMEOUT_MSG = 8;
- static final int PAUSE_TIMEOUT_MSG = 9;
- static final int IDLE_TIMEOUT_MSG = 10;
- static final int IDLE_NOW_MSG = 11;
static final int SERVICE_TIMEOUT_MSG = 12;
static final int UPDATE_TIME_ZONE = 13;
static final int SHOW_UID_ERROR_MSG = 14;
static final int IM_FEELING_LUCKY_MSG = 15;
- static final int LAUNCH_TIMEOUT_MSG = 16;
- static final int DESTROY_TIMEOUT_MSG = 17;
- static final int RESUME_TOP_ACTIVITY_MSG = 19;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
+ static final int POST_HEAVY_NOTIFICATION_MSG = 24;
+ static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
+ static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
+ static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
AlertDialog mUidAlert;
@@ -1053,13 +967,38 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
false, false, MY_PID, Process.SYSTEM_UID);
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
- mContext, proc, (HistoryRecord)data.get("activity"));
+ mContext, proc, (ActivityRecord)data.get("activity"));
d.show();
proc.anrDialog = d;
}
ensureBootCompleted();
} break;
+ case SHOW_STRICT_MODE_VIOLATION_MSG: {
+ HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
+ synchronized (ActivityManagerService.this) {
+ ProcessRecord proc = (ProcessRecord) data.get("app");
+ if (proc == null) {
+ Slog.e(TAG, "App not found when showing strict mode dialog.");
+ break;
+ }
+ if (proc.crashDialog != null) {
+ Slog.e(TAG, "App already has strict mode dialog: " + proc);
+ return;
+ }
+ AppErrorResult res = (AppErrorResult) data.get("result");
+ if (!mSleeping && !mShuttingDown) {
+ Dialog d = new StrictModeViolationDialog(mContext, res, proc);
+ d.show();
+ proc.crashDialog = d;
+ } else {
+ // The device is asleep, so just pretend that the user
+ // saw a crash dialog and hit "force quit".
+ res.set(0);
+ }
+ }
+ ensureBootCompleted();
+ } break;
case SHOW_FACTORY_ERROR_MSG: {
Dialog d = new FactoryErrorDialog(
mContext, msg.getData().getCharSequence("msg"));
@@ -1114,38 +1053,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
broadcastTimeout();
}
} break;
- case PAUSE_TIMEOUT_MSG: {
- IBinder token = (IBinder)msg.obj;
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- Slog.w(TAG, "Activity pause timeout for " + token);
- activityPaused(token, null, true);
- } break;
- case IDLE_TIMEOUT_MSG: {
- if (mDidDexOpt) {
- mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
- nmsg.obj = msg.obj;
- mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
- return;
- }
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- IBinder token = (IBinder)msg.obj;
- Slog.w(TAG, "Activity idle timeout for " + token);
- activityIdleInternal(token, true, null);
- } break;
- case DESTROY_TIMEOUT_MSG: {
- IBinder token = (IBinder)msg.obj;
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- Slog.w(TAG, "Activity destroy timeout for " + token);
- activityDestroyed(token);
- } break;
- case IDLE_NOW_MSG: {
- IBinder token = (IBinder)msg.obj;
- activityIdle(token, null);
- } break;
case SERVICE_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
@@ -1188,25 +1095,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mUidAlert = null;
}
} break;
- case LAUNCH_TIMEOUT_MSG: {
- if (mDidDexOpt) {
- mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
- return;
- }
- synchronized (ActivityManagerService.this) {
- if (mLaunchingActivity.isHeld()) {
- Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
- mLaunchingActivity.release();
- }
- }
- } break;
- case RESUME_TOP_ACTIVITY_MSG: {
- synchronized (ActivityManagerService.this) {
- resumeTopActivityLocked(null);
- }
- } break;
case PROC_START_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
@@ -1236,6 +1124,72 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
case FINALIZE_PENDING_INTENT_MSG: {
((PendingIntentRecord)msg.obj).completeFinalize();
} break;
+ case POST_HEAVY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ ActivityRecord root = (ActivityRecord)msg.obj;
+ ProcessRecord process = root.app;
+ if (process == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(process.info.packageName, 0);
+ String text = mContext.getString(R.string.heavy_weight_notification,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_sys_adb; //context.getApplicationInfo().icon;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.tickerText = text;
+ notification.defaults = 0; // please be quiet
+ notification.sound = null;
+ notification.vibrate = null;
+ notification.setLatestEventInfo(context, text,
+ mContext.getText(R.string.heavy_weight_notification_detail),
+ PendingIntent.getActivity(mContext, 0, root.intent,
+ PendingIntent.FLAG_CANCEL_CURRENT));
+
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotification("android", R.string.heavy_weight_notification,
+ notification, outId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for heavy-weight app", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for heavy notification", e);
+ }
+ } break;
+ case CANCEL_HEAVY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotification("android",
+ R.string.heavy_weight_notification);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
+ case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
+ synchronized (ActivityManagerService.this) {
+ checkExcessiveWakeLocksLocked(true);
+ removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ if (mSleeping) {
+ Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+ }
+ }
+ } break;
}
}
};
@@ -1299,11 +1253,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Context context = at.getSystemContext();
m.mContext = context;
m.mFactoryTest = factoryTest;
- PowerManager pm =
- (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- m.mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
- m.mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
- m.mLaunchingActivity.setReferenceCounted(false);
+ m.mMainStack = new ActivityStack(m, context, true);
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
@@ -1335,6 +1285,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ android.os.Process.setCanSelfBackground(false);
ActivityManagerService m = new ActivityManagerService();
@@ -1592,7 +1543,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return mAppBindArgs;
}
- private final void setFocusedActivityLocked(HistoryRecord r) {
+ final void setFocusedActivityLocked(ActivityRecord r) {
if (mFocusedActivity != r) {
mFocusedActivity = r;
mWindowManager.setFocusedApp(r, true);
@@ -1677,65 +1628,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void updateLruProcessLocked(ProcessRecord app,
+ final void updateLruProcessLocked(ProcessRecord app,
boolean oomAdj, boolean updateActivityTime) {
mLruSeq++;
updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0);
}
- private final boolean updateLRUListLocked(HistoryRecord r) {
- final boolean hadit = mLRUActivities.remove(r);
- mLRUActivities.add(r);
- return hadit;
- }
-
- private final HistoryRecord topRunningActivityLocked(HistoryRecord notTop) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (!r.finishing && r != notTop) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- private final HistoryRecord topRunningNonDelayedActivityLocked(HistoryRecord notTop) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (!r.finishing && !r.delayedResume && r != notTop) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- /**
- * This is a simplified version of topRunningActivityLocked that provides a number of
- * optional skip-over modes. It is intended for use with the ActivityController hook only.
- *
- * @param token If non-null, any history records matching this token will be skipped.
- * @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
- *
- * @return Returns the HistoryRecord of the next activity on the stack.
- */
- private final HistoryRecord topRunningActivityLocked(IBinder token, int taskId) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- // Note: the taskId check depends on real taskId fields being non-zero
- if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- private final ProcessRecord getProcessRecordLocked(
+ final ProcessRecord getProcessRecordLocked(
String processName, int uid) {
if (uid == Process.SYSTEM_UID) {
// The system gets to run in any process. If there are multiple
@@ -1749,8 +1648,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return proc;
}
- private void ensurePackageDexOpt(String packageName) {
- IPackageManager pm = ActivityThread.getPackageManager();
+ void ensurePackageDexOpt(String packageName) {
+ IPackageManager pm = AppGlobals.getPackageManager();
try {
if (pm.performDexOpt(packageName)) {
mDidDexOpt = true;
@@ -1759,157 +1658,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private boolean isNextTransitionForward() {
+ boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
|| transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
|| transit == WindowManagerPolicy.TRANSIT_TASK_TO_FRONT;
}
- private final boolean realStartActivityLocked(HistoryRecord r,
- ProcessRecord app, boolean andResume, boolean checkConfig)
- throws RemoteException {
-
- r.startFreezingScreenLocked(app, 0);
- mWindowManager.setAppVisibility(r, true);
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order. Note that
- // as a result of this, it can call back into the activity
- // manager with a new orientation. We don't care about that,
- // because the activity is not currently running so we are
- // just restarting it anyway.
- if (checkConfig) {
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- r.mayFreezeScreenLocked(app) ? r : null);
- updateConfigurationLocked(config, r);
- }
-
- r.app = app;
-
- if (localLOGV) Slog.v(TAG, "Launching: " + r);
-
- int idx = app.activities.indexOf(r);
- if (idx < 0) {
- app.activities.add(r);
- }
- updateLruProcessLocked(app, true, true);
-
- try {
- if (app.thread == null) {
- throw new RemoteException();
- }
- List<ResultInfo> results = null;
- List<Intent> newIntents = null;
- if (andResume) {
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
- + " icicle=" + r.icicle
- + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
- if (andResume) {
- EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
- }
- if (r.isHomeActivity) {
- mHomeProcess = app;
- }
- ensurePackageDexOpt(r.intent.getComponent().getPackageName());
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
- System.identityHashCode(r),
- r.info, r.icicle, results, newIntents, !andResume,
- isNextTransitionForward());
- } catch (RemoteException e) {
- if (r.launchFailed) {
- // This is the second time we failed -- finish activity
- // and give up.
- Slog.e(TAG, "Second failure launching "
- + r.intent.getComponent().flattenToShortString()
- + ", giving up", e);
- appDiedLocked(app, app.pid, app.thread);
- requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
- "2nd-crash");
- return false;
- }
-
- // This is the first time we failed -- restart process and
- // retry.
- app.activities.remove(r);
- throw e;
- }
-
- r.launchFailed = false;
- if (updateLRUListLocked(r)) {
- Slog.w(TAG, "Activity " + r
- + " being launched, but already in LRU list");
- }
-
- if (andResume) {
- // As part of the process of launching, ActivityThread also performs
- // a resume.
- r.state = ActivityState.RESUMED;
- r.icicle = null;
- r.haveState = false;
- r.stopped = false;
- mResumedActivity = r;
- r.task.touchActiveTime();
- completeResumeLocked(r);
- pauseIfSleepingLocked();
- } else {
- // This activity is not starting in the resumed state... which
- // should look like we asked it to pause+stop (but remain visible),
- // and it has done so and reported back the current icicle and
- // other state.
- r.state = ActivityState.STOPPED;
- r.stopped = true;
- }
-
- // Launch the new version setup screen if needed. We do this -after-
- // launching the initial activity (that is, home), so that it can have
- // a chance to initialize itself while in the background, making the
- // switch back to it faster and look better.
- startSetupActivityLocked();
-
- return true;
- }
-
- private final void startSpecificActivityLocked(HistoryRecord r,
- boolean andResume, boolean checkConfig) {
- // Is this activity's application already running?
- ProcessRecord app = getProcessRecordLocked(r.processName,
- r.info.applicationInfo.uid);
-
- if (r.startTime == 0) {
- r.startTime = SystemClock.uptimeMillis();
- if (mInitialStartTime == 0) {
- mInitialStartTime = r.startTime;
- }
- } else if (mInitialStartTime == 0) {
- mInitialStartTime = SystemClock.uptimeMillis();
- }
-
- if (app != null && app.thread != null) {
- try {
- realStartActivityLocked(r, app, andResume, checkConfig);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when starting activity "
- + r.intent.getComponent().flattenToShortString(), e);
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent(), false);
- }
-
- private final ProcessRecord startProcessLocked(String processName,
+ final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
ProcessRecord app = getProcessRecordLocked(processName, info.uid);
@@ -2055,7 +1811,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
hostingNameStr != null ? hostingNameStr : "");
if (app.persistent) {
- Watchdog.getInstance().processStarted(app, app.processName, pid);
+ Watchdog.getInstance().processStarted(app.processName, pid);
}
StringBuilder buf = mStringBuilder;
@@ -2110,365 +1866,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
- if (mPausingActivity != null) {
- RuntimeException e = new RuntimeException();
- Slog.e(TAG, "Trying to pause when pause is already pending for "
- + mPausingActivity, e);
- }
- HistoryRecord prev = mResumedActivity;
- if (prev == null) {
- RuntimeException e = new RuntimeException();
- Slog.e(TAG, "Trying to pause when nothing is resumed", e);
- resumeTopActivityLocked(null);
- return;
- }
- if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
- mResumedActivity = null;
- mPausingActivity = prev;
- mLastPausedActivity = prev;
- prev.state = ActivityState.PAUSING;
- prev.task.touchActiveTime();
-
- updateCpuStats();
-
- if (prev.app != null && prev.app.thread != null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
- try {
- EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
- System.identityHashCode(prev),
- prev.shortComponentName);
- prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
- prev.configChangeFlags);
- updateUsageStats(prev, false);
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- }
- } else {
- mPausingActivity = null;
- mLastPausedActivity = null;
- }
-
- // If we are not going to sleep, we want to ensure the device is
- // awake until the next activity is started.
- if (!mSleeping && !mShuttingDown) {
- mLaunchingActivity.acquire();
- if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
- // To be safe, don't allow the wake lock to be held for too long.
- Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
- }
- }
-
-
- if (mPausingActivity != null) {
- // Have the window manager pause its key dispatching until the new
- // activity has started. If we're pausing the activity just because
- // the screen is being turned off and the UI is sleeping, don't interrupt
- // key dispatch; the same activity will pick it up again on wakeup.
- if (!uiSleeping) {
- prev.pauseKeyDispatchingLocked();
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
- }
-
- // Schedule a pause timeout in case the app doesn't respond.
- // We don't give it much time because this directly impacts the
- // responsiveness seen by the user.
- Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
- msg.obj = prev;
- mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
- if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
- } else {
- // This activity failed to schedule the
- // pause, so just treat it as being paused now.
- if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
- resumeTopActivityLocked(null);
- }
- }
-
- private final void completePauseLocked() {
- HistoryRecord prev = mPausingActivity;
- if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
-
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
- prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
- } else if (prev.app != null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
- if (prev.waitingVisible) {
- prev.waitingVisible = false;
- mWaitingVisibleActivities.remove(prev);
- if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
- TAG, "Complete pause, no longer waiting: " + prev);
- }
- if (prev.configDestroy) {
- // The previous is being paused because the configuration
- // is changing, which means it is actually stopping...
- // To juggle the fact that we are also starting a new
- // instance right now, we need to first completely stop
- // the current instance before starting the new one.
- if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
- destroyActivityLocked(prev, true);
- } else {
- mStoppingActivities.add(prev);
- if (mStoppingActivities.size() > 3) {
- // If we already have a few activities waiting to stop,
- // then give up on things going idle and start clearing
- // them out.
- if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
- }
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
- prev = null;
- }
- mPausingActivity = null;
- }
-
- if (!mSleeping && !mShuttingDown) {
- resumeTopActivityLocked(prev);
- } else {
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
- }
- if (mShuttingDown) {
- notifyAll();
- }
- }
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
- }
-
- if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) {
- long diff = 0;
- synchronized (mProcessStatsThread) {
- diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume;
- }
- if (diff > 0) {
- BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
- synchronized (bsi) {
- BatteryStatsImpl.Uid.Proc ps =
- bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
- prev.info.packageName);
- if (ps != null) {
- ps.addForegroundTimeLocked(diff);
- }
- }
- }
- }
- prev.cpuTimeAtResume = 0; // reset it
- }
-
- /**
- * Once we know that we have asked an application to put an activity in
- * the resumed state (either by launching it or explicitly telling it),
- * this function updates the rest of our state to match that fact.
- */
- private final void completeResumeLocked(HistoryRecord next) {
- next.idle = false;
- next.results = null;
- next.newIntents = null;
-
- // schedule an idle timeout in case the app doesn't do it for us.
- Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
- msg.obj = next;
- mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
-
- if (false) {
- // The activity was never told to pause, so just keep
- // things going as-is. To maintain our own state,
- // we need to emulate it coming back and saying it is
- // idle.
- msg = mHandler.obtainMessage(IDLE_NOW_MSG);
- msg.obj = next;
- mHandler.sendMessage(msg);
- }
-
- reportResumedActivityLocked(next);
-
- next.thumbnail = null;
- setFocusedActivityLocked(next);
- next.resumeKeyDispatchingLocked();
- ensureActivitiesVisibleLocked(null, 0);
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
-
- // Mark the point when the activity is resuming
- // TODO: To be more accurate, the mark should be before the onCreate,
- // not after the onResume. But for subsequent starts, onResume is fine.
- if (next.app != null) {
- synchronized (mProcessStatsThread) {
- next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid);
- }
- } else {
- next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
- }
- }
-
- /**
- * Make sure that all activities that need to be visible (that is, they
- * currently can be seen by the user) actually are.
- */
- private final void ensureActivitiesVisibleLocked(HistoryRecord top,
- HistoryRecord starting, String onlyThisProcess, int configChanges) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "ensureActivitiesVisible behind " + top
- + " configChanges=0x" + Integer.toHexString(configChanges));
-
- // If the top activity is not fullscreen, then we need to
- // make sure any activities under it are now visible.
- final int count = mHistory.size();
- int i = count-1;
- while (mHistory.get(i) != top) {
- i--;
- }
- HistoryRecord r;
- boolean behindFullscreen = false;
- for (; i>=0; i--) {
- r = (HistoryRecord)mHistory.get(i);
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Make visible? " + r + " finishing=" + r.finishing
- + " state=" + r.state);
- if (r.finishing) {
- continue;
- }
-
- final boolean doThisProcess = onlyThisProcess == null
- || onlyThisProcess.equals(r.processName);
-
- // First: if this is not the current activity being started, make
- // sure it matches the current configuration.
- if (r != starting && doThisProcess) {
- ensureActivityConfigurationLocked(r, 0);
- }
-
- if (r.app == null || r.app.thread == null) {
- if (onlyThisProcess == null
- || onlyThisProcess.equals(r.processName)) {
- // This activity needs to be visible, but isn't even
- // running... get it started, but don't resume it
- // at this point.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Start and freeze screen for " + r);
- if (r != starting) {
- r.startFreezingScreenLocked(r.app, configChanges);
- }
- if (!r.visible) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Starting and making visible: " + r);
- mWindowManager.setAppVisibility(r, true);
- }
- if (r != starting) {
- startSpecificActivityLocked(r, false, false);
- }
- }
-
- } else if (r.visible) {
- // If this activity is already visible, then there is nothing
- // else to do here.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Skipping: already visible at " + r);
- r.stopFreezingScreenLocked(false);
-
- } else if (onlyThisProcess == null) {
- // This activity is not currently visible, but is running.
- // Tell it to become visible.
- r.visible = true;
- if (r.state != ActivityState.RESUMED && r != starting) {
- // If this activity is paused, tell it
- // to now show its window.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Making visible and scheduling visibility: " + r);
- try {
- mWindowManager.setAppVisibility(r, true);
- r.app.thread.scheduleWindowVisibility(r, true);
- r.stopFreezingScreenLocked(false);
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making visibile: "
- + r.intent.getComponent(), e);
- }
- }
- }
-
- // Aggregate current change flags.
- configChanges |= r.configChangeFlags;
-
- if (r.fullscreen) {
- // At this point, nothing else needs to be shown
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Stopping: fullscreen at " + r);
- behindFullscreen = true;
- i--;
- break;
- }
- }
-
- // Now for any activities that aren't visible to the user, make
- // sure they no longer are keeping the screen frozen.
- while (i >= 0) {
- r = (HistoryRecord)mHistory.get(i);
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Make invisible? " + r + " finishing=" + r.finishing
- + " state=" + r.state
- + " behindFullscreen=" + behindFullscreen);
- if (!r.finishing) {
- if (behindFullscreen) {
- if (r.visible) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Making invisible: " + r);
- r.visible = false;
- try {
- mWindowManager.setAppVisibility(r, false);
- if ((r.state == ActivityState.STOPPING
- || r.state == ActivityState.STOPPED)
- && r.app != null && r.app.thread != null) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Scheduling invisibility: " + r);
- r.app.thread.scheduleWindowVisibility(r, false);
- }
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making hidden: "
- + r.intent.getComponent(), e);
- }
- } else {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Already invisible: " + r);
- }
- } else if (r.fullscreen) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Now behindFullscreen: " + r);
- behindFullscreen = true;
- }
- }
- i--;
- }
- }
-
- /**
- * Version of ensureActivitiesVisible that can easily be called anywhere.
- */
- private final void ensureActivitiesVisibleLocked(HistoryRecord starting,
- int configChanges) {
- HistoryRecord r = topRunningActivityLocked(null);
- if (r != null) {
- ensureActivitiesVisibleLocked(r, starting, null, configChanges);
- }
- }
-
- private void updateUsageStats(HistoryRecord resumedComponent, boolean resumed) {
+ void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) {
if (resumed) {
mUsageStatsService.noteResumeComponent(resumedComponent.realActivity);
} else {
@@ -2476,7 +1874,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private boolean startHomeActivityLocked() {
+ boolean startHomeActivityLocked() {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -2503,7 +1901,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivityLocked(null, intent, null, null, 0, aInfo,
+ mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
null, null, 0, 0, 0, false, false);
}
}
@@ -2515,7 +1913,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
/**
* Starts the "new version setup screen" if appropriate.
*/
- private void startSetupActivityLocked() {
+ void startSetupActivityLocked() {
// Only do this once per boot.
if (mCheckedForSetup) {
return;
@@ -2559,14 +1957,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
- startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
+ mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
null, null, 0, 0, 0, false, false);
}
}
}
}
- private void reportResumedActivityLocked(HistoryRecord r) {
+ void reportResumedActivityLocked(ActivityRecord r) {
//Slog.i(TAG, "**** REPORT RESUME: " + r);
final int identHash = System.identityHashCode(r);
@@ -2585,1210 +1983,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
mWatchers.finishBroadcast();
}
-
- /**
- * Ensure that the top activity in the stack is resumed.
- *
- * @param prev The previously resumed activity, for when in the process
- * of pausing; can be null to call from elsewhere.
- *
- * @return Returns true if something is being resumed, or false if
- * nothing happened.
- */
- private final boolean resumeTopActivityLocked(HistoryRecord prev) {
- // Find the first activity that is not finishing.
- HistoryRecord next = topRunningActivityLocked(null);
-
- // Remember how we'll process this pause/resume situation, and ensure
- // that the state is reset however we wind up proceeding.
- final boolean userLeaving = mUserLeaving;
- mUserLeaving = false;
-
- if (next == null) {
- // There are no more activities! Let's just start up the
- // Launcher...
- return startHomeActivityLocked();
- }
-
- next.delayedResume = false;
-
- // If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return false;
- }
-
- // If we are sleeping, and there is no resumed activity, and the top
- // activity is paused, well that is the state we want.
- if ((mSleeping || mShuttingDown)
- && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return false;
- }
-
- // The activity may be waiting for stop, but that is no longer
- // appropriate for it.
- mStoppingActivities.remove(next);
- mWaitingVisibleActivities.remove(next);
-
- if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
-
- // If we are currently pausing an activity, then don't do anything
- // until that is done.
- if (mPausingActivity != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
- return false;
- }
-
- // We need to start pausing the current activity so the top one
- // can be resumed...
- if (mResumedActivity != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
- startPausingLocked(userLeaving, false);
- return true;
- }
-
- if (prev != null && prev != next) {
- if (!prev.waitingVisible && next != null && !next.nowVisible) {
- prev.waitingVisible = true;
- mWaitingVisibleActivities.add(prev);
- if (DEBUG_SWITCH) Slog.v(
- TAG, "Resuming top, waiting visible to hide: " + prev);
- } else {
- // The next activity is already visible, so hide the previous
- // activity's windows right now so we can show the new one ASAP.
- // We only do this if the previous is finishing, which should mean
- // it is on top of the one being resumed so hiding it quickly
- // is good. Otherwise, we want to do the normal route of allowing
- // the resumed activity to be shown so we can decide if the
- // previous should actually be hidden depending on whether the
- // new one is found to be full-screen or not.
- if (prev.finishing) {
- mWindowManager.setAppVisibility(prev, false);
- if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
- + prev + ", waitingVisible="
- + (prev != null ? prev.waitingVisible : null)
- + ", nowVisible=" + next.nowVisible);
- } else {
- if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
- + prev + ", waitingVisible="
- + (prev != null ? prev.waitingVisible : null)
- + ", nowVisible=" + next.nowVisible);
- }
- }
- }
- // We are starting up the next activity, so tell the window manager
- // that the previous one will be hidden soon. This way it can know
- // to ignore it when computing the desired screen orientation.
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare close transition: prev=" + prev);
- if (mNoAnimActivities.contains(prev)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
- : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
- }
- mWindowManager.setAppWillBeHidden(prev);
- mWindowManager.setAppVisibility(prev, false);
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: prev=" + prev);
- if (mNoAnimActivities.contains(next)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
- : WindowManagerPolicy.TRANSIT_TASK_OPEN);
- }
- }
- if (false) {
- mWindowManager.setAppWillBeHidden(prev);
- mWindowManager.setAppVisibility(prev, false);
- }
- } else if (mHistory.size() > 1) {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: no previous");
- if (mNoAnimActivities.contains(next)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
- }
- }
-
- if (next.app != null && next.app.thread != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
-
- // This activity is now becoming visible.
- mWindowManager.setAppVisibility(next, true);
-
- HistoryRecord lastResumedActivity = mResumedActivity;
- ActivityState lastState = next.state;
-
- updateCpuStats();
-
- next.state = ActivityState.RESUMED;
- mResumedActivity = next;
- next.task.touchActiveTime();
- updateLruProcessLocked(next.app, true, true);
- updateLRUListLocked(next);
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean updated;
- synchronized (this) {
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next : null);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- updated = updateConfigurationLocked(config, next);
- }
- if (!updated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- HistoryRecord nextNext = topRunningActivityLocked(null);
- if (DEBUG_SWITCH) Slog.i(TAG,
- "Activity config changed during resume: " + next
- + ", new next: " + nextNext);
- if (nextNext != next) {
- // Do over!
- mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
- }
- setFocusedActivityLocked(next);
- ensureActivitiesVisibleLocked(null, 0);
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return true;
- }
-
- try {
- // Deliver all pending results.
- ArrayList a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Delivering results to " + next
- + ": " + a);
- next.app.thread.scheduleSendResult(next, a);
- }
- }
-
- if (next.newIntents != null) {
- next.app.thread.scheduleNewIntent(next.newIntents, next);
- }
-
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
- System.identityHashCode(next),
- next.task.taskId, next.shortComponentName);
-
- next.app.thread.scheduleResumeActivity(next,
- isNextTransitionForward());
-
- pauseIfSleepingLocked();
-
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- next.state = lastState;
- mResumedActivity = lastResumedActivity;
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_ICON) {
- mWindowManager.setAppStartingWindow(
- next, next.packageName, next.theme,
- next.nonLocalizedLabel,
- next.labelRes, next.icon, null, true);
- }
- }
- startSpecificActivityLocked(next, true, false);
- return true;
- }
-
- // From this point on, if something goes wrong there is no way
- // to recover the activity.
- try {
- next.visible = true;
- completeResumeLocked(next);
- } catch (Exception e) {
- // If any exception gets thrown, toss away this
- // activity and try the next one.
- Slog.w(TAG, "Exception thrown during resume of " + next, e);
- requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
- "resume-exception");
- return true;
- }
-
- // Didn't need to use the icicle, and it is now out of date.
- next.icicle = null;
- next.haveState = false;
- next.stopped = false;
-
- } else {
- // Whoops, need to restart this activity!
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_ICON) {
- mWindowManager.setAppStartingWindow(
- next, next.packageName, next.theme,
- next.nonLocalizedLabel,
- next.labelRes, next.icon, null, true);
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
- }
- startSpecificActivityLocked(next, true, true);
- }
-
- return true;
- }
-
- private final void startActivityLocked(HistoryRecord r, boolean newTask,
- boolean doResume) {
- final int NH = mHistory.size();
-
- int addPos = -1;
-
- if (!newTask) {
- // If starting in an existing task, find where that is...
- HistoryRecord next = null;
- boolean startIt = true;
- for (int i = NH-1; i >= 0; i--) {
- HistoryRecord p = (HistoryRecord)mHistory.get(i);
- if (p.finishing) {
- continue;
- }
- if (p.task == r.task) {
- // Here it is! Now, if this is not yet visible to the
- // user, then just add it without starting; it will
- // get started when the user navigates back to it.
- addPos = i+1;
- if (!startIt) {
- mHistory.add(addPos, r);
- r.inHistory = true;
- r.task.numActivities++;
- mWindowManager.addAppToken(addPos, r, r.task.taskId,
- r.info.screenOrientation, r.fullscreen);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- return;
- }
- break;
- }
- if (p.fullscreen) {
- startIt = false;
- }
- next = p;
- }
- }
-
- // Place a new activity at top of stack, so it is next to interact
- // with the user.
- if (addPos < 0) {
- addPos = mHistory.size();
- }
-
- // If we are not placing the new activity frontmost, we do not want
- // to deliver the onUserLeaving callback to the actual frontmost
- // activity
- if (addPos < NH) {
- mUserLeaving = false;
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false");
- }
-
- // Slot the activity into the history stack and proceed
- mHistory.add(addPos, r);
- r.inHistory = true;
- r.frontOfTask = newTask;
- r.task.numActivities++;
- if (NH > 0) {
- // We want to show the starting preview window if we are
- // switching to a new task, or the next activity's process is
- // not currently running.
- boolean showStartingIcon = newTask;
- ProcessRecord proc = r.app;
- if (proc == null) {
- proc = mProcessNames.get(r.processName, r.info.applicationInfo.uid);
- }
- if (proc == null || proc.thread == null) {
- showStartingIcon = true;
- }
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: starting " + r);
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- mNoAnimActivities.add(r);
- } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_OPEN);
- mNoAnimActivities.remove(r);
- } else {
- mWindowManager.prepareAppTransition(newTask
- ? WindowManagerPolicy.TRANSIT_TASK_OPEN
- : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
- mNoAnimActivities.remove(r);
- }
- mWindowManager.addAppToken(
- addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
- boolean doShow = true;
- if (newTask) {
- // Even though this activity is starting fresh, we still need
- // to reset it to make sure we apply affinities to move any
- // existing activities from other tasks in to it.
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((r.intent.getFlags()
- &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- resetTaskIfNeededLocked(r, r);
- doShow = topRunningNonDelayedActivityLocked(null) == r;
- }
- }
- if (SHOW_APP_STARTING_ICON && doShow) {
- // Figure out if we are transitioning from another activity that is
- // "has the same starting icon" as the next one. This allows the
- // window manager to keep the previous window it had previously
- // created, if it still had one.
- HistoryRecord prev = mResumedActivity;
- if (prev != null) {
- // We don't want to reuse the previous starting preview if:
- // (1) The current activity is in a different task.
- if (prev.task != r.task) prev = null;
- // (2) The current activity is already displayed.
- else if (prev.nowVisible) prev = null;
- }
- mWindowManager.setAppStartingWindow(
- r, r.packageName, r.theme, r.nonLocalizedLabel,
- r.labelRes, r.icon, prev, showStartingIcon);
- }
- } else {
- // If this is the first activity, don't do any fancy animations,
- // because there is nothing for it to animate on top of.
- mWindowManager.addAppToken(addPos, r, r.task.taskId,
- r.info.screenOrientation, r.fullscreen);
- }
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- }
-
- /**
- * Perform clear operation as requested by
- * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
- * stack to the given task, then look for
- * an instance of that activity in the stack and, if found, finish all
- * activities on top of it and return the instance.
- *
- * @param newR Description of the new activity being started.
- * @return Returns the old activity that should be continue to be used,
- * or null if none was found.
- */
- private final HistoryRecord performClearTaskLocked(int taskId,
- HistoryRecord newR, int launchFlags, boolean doClear) {
- int i = mHistory.size();
-
- // First find the requested task.
- while (i > 0) {
- i--;
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (r.task.taskId == taskId) {
- i++;
- break;
- }
- }
-
- // Now clear it.
- while (i > 0) {
- i--;
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (r.task.taskId != taskId) {
- return null;
- }
- if (r.realActivity.equals(newR.realActivity)) {
- // Here it is! Now finish everything in front...
- HistoryRecord ret = r;
- if (doClear) {
- while (i < (mHistory.size()-1)) {
- i++;
- r = (HistoryRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- i--;
- }
- }
- }
-
- // Finally, if this is a normal launch mode (that is, not
- // expecting onNewIntent()), then we will finish the current
- // instance of the activity so a new fresh one can be started.
- if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
- && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
- if (!ret.finishing) {
- int index = indexOfTokenLocked(ret);
- if (index >= 0) {
- finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
- null, "clear");
- }
- return null;
- }
- }
-
- return ret;
- }
- }
-
- return null;
- }
-
- /**
- * Find the activity in the history stack within the given task. Returns
- * the index within the history at which it's found, or < 0 if not found.
- */
- private final int findActivityInHistoryLocked(HistoryRecord r, int task) {
- int i = mHistory.size();
- while (i > 0) {
- i--;
- HistoryRecord candidate = (HistoryRecord)mHistory.get(i);
- if (candidate.task.taskId != task) {
- break;
- }
- if (candidate.realActivity.equals(r.realActivity)) {
- return i;
- }
- }
-
- return -1;
- }
-
- /**
- * Reorder the history stack so that the activity at the given index is
- * brought to the front.
- */
- private final HistoryRecord moveActivityToFrontLocked(int where) {
- HistoryRecord newTop = (HistoryRecord)mHistory.remove(where);
- int top = mHistory.size();
- HistoryRecord oldTop = (HistoryRecord)mHistory.get(top-1);
- mHistory.add(top, newTop);
- oldTop.frontOfTask = false;
- newTop.frontOfTask = true;
- return newTop;
- }
-
- /**
- * Deliver a new Intent to an existing activity, so that its onNewIntent()
- * method will be called at the proper time.
- */
- private final void deliverNewIntentLocked(HistoryRecord r, Intent intent) {
- boolean sent = false;
- if (r.state == ActivityState.RESUMED
- && r.app != null && r.app.thread != null) {
- try {
- ArrayList<Intent> ar = new ArrayList<Intent>();
- ar.add(new Intent(intent));
- r.app.thread.scheduleNewIntent(ar, r);
- sent = true;
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending new intent to " + r, e);
- }
- }
- if (!sent) {
- r.addNewIntentLocked(new Intent(intent));
- }
- }
-
- private final void logStartActivity(int tag, HistoryRecord r,
- TaskRecord task) {
- EventLog.writeEvent(tag,
- System.identityHashCode(r), task.taskId,
- r.shortComponentName, r.intent.getAction(),
- r.intent.getType(), r.intent.getDataString(),
- r.intent.getFlags());
- }
-
- private final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType,
- Uri[] grantedUriPermissions,
- int grantedMode, ActivityInfo aInfo, IBinder resultTo,
- String resultWho, int requestCode,
- int callingPid, int callingUid, boolean onlyIfNeeded,
- boolean componentSpecified) {
- Slog.i(TAG, "Starting activity: " + intent);
-
- HistoryRecord sourceRecord = null;
- HistoryRecord resultRecord = null;
- if (resultTo != null) {
- int index = indexOfTokenLocked(resultTo);
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Sending result to " + resultTo + " (index " + index + ")");
- if (index >= 0) {
- sourceRecord = (HistoryRecord)mHistory.get(index);
- if (requestCode >= 0 && !sourceRecord.finishing) {
- resultRecord = sourceRecord;
- }
- }
- }
-
- int launchFlags = intent.getFlags();
-
- if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
- && sourceRecord != null) {
- // Transfer the result target from the source activity to the new
- // one being started, including any failures.
- if (requestCode >= 0) {
- return START_FORWARD_AND_REQUEST_CONFLICT;
- }
- resultRecord = sourceRecord.resultTo;
- resultWho = sourceRecord.resultWho;
- requestCode = sourceRecord.requestCode;
- sourceRecord.resultTo = null;
- if (resultRecord != null) {
- resultRecord.removeResultsLocked(
- sourceRecord, resultWho, requestCode);
- }
- }
-
- int err = START_SUCCESS;
-
- if (intent.getComponent() == null) {
- // We couldn't find a class that can handle the given Intent.
- // That's the end of that!
- err = START_INTENT_NOT_RESOLVED;
- }
-
- if (err == START_SUCCESS && aInfo == null) {
- // We couldn't find the specific class specified in the Intent.
- // Also the end of the line.
- err = START_CLASS_NOT_FOUND;
- }
-
- ProcessRecord callerApp = null;
- if (err == START_SUCCESS && caller != null) {
- callerApp = getRecordForAppLocked(caller);
- if (callerApp != null) {
- callingPid = callerApp.pid;
- callingUid = callerApp.info.uid;
- } else {
- Slog.w(TAG, "Unable to find app for caller " + caller
- + " (pid=" + callingPid + ") when starting: "
- + intent.toString());
- err = START_PERMISSION_DENIED;
- }
- }
-
- if (err != START_SUCCESS) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- return err;
- }
-
- final int perm = checkComponentPermission(aInfo.permission, callingPid,
- callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- String msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + aInfo.permission;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- if (mController != null) {
- boolean abort = false;
- try {
- // The Intent we give to the watcher has the extra data
- // stripped off, since it can contain private information.
- Intent watchIntent = intent.cloneFilter();
- abort = !mController.activityStarting(watchIntent,
- aInfo.applicationInfo.packageName);
- } catch (RemoteException e) {
- mController = null;
- }
-
- if (abort) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- // We pretend to the caller that it was really started, but
- // they will just get a cancel result.
- return START_SUCCESS;
- }
- }
-
- HistoryRecord r = new HistoryRecord(this, callerApp, callingUid,
- intent, resolvedType, aInfo, mConfiguration,
- resultRecord, resultWho, requestCode, componentSpecified);
-
- if (mResumedActivity == null
- || mResumedActivity.info.applicationInfo.uid != callingUid) {
- if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
- PendingActivityLaunch pal = new PendingActivityLaunch();
- pal.r = r;
- pal.sourceRecord = sourceRecord;
- pal.grantedUriPermissions = grantedUriPermissions;
- pal.grantedMode = grantedMode;
- pal.onlyIfNeeded = onlyIfNeeded;
- mPendingActivityLaunches.add(pal);
- return START_SWITCHES_CANCELED;
- }
- }
-
- if (mDidAppSwitch) {
- // This is the second allowed switch since we stopped switches,
- // so now just generally allow switches. Use case: user presses
- // home (switches disabled, switch to home, mDidAppSwitch now true);
- // user taps a home icon (coming from home so allowed, we hit here
- // and now allow anyone to switch again).
- mAppSwitchesAllowedTime = 0;
- } else {
- mDidAppSwitch = true;
- }
-
- doPendingActivityLaunchesLocked(false);
-
- return startActivityUncheckedLocked(r, sourceRecord,
- grantedUriPermissions, grantedMode, onlyIfNeeded, true);
- }
-
- private final void doPendingActivityLaunchesLocked(boolean doResume) {
+ final void doPendingActivityLaunchesLocked(boolean doResume) {
final int N = mPendingActivityLaunches.size();
if (N <= 0) {
return;
}
for (int i=0; i<N; i++) {
PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
- startActivityUncheckedLocked(pal.r, pal.sourceRecord,
+ mMainStack.startActivityUncheckedLocked(pal.r, pal.sourceRecord,
pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded,
doResume && i == (N-1));
}
mPendingActivityLaunches.clear();
}
- private final int startActivityUncheckedLocked(HistoryRecord r,
- HistoryRecord sourceRecord, Uri[] grantedUriPermissions,
- int grantedMode, boolean onlyIfNeeded, boolean doResume) {
- final Intent intent = r.intent;
- final int callingUid = r.launchedFromUid;
-
- int launchFlags = intent.getFlags();
-
- // We'll invoke onUserLeaving before onPause only if the launching
- // activity did not explicitly state that this is an automated launch.
- mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
- if (DEBUG_USER_LEAVING) Slog.v(TAG,
- "startActivity() => mUserLeaving=" + mUserLeaving);
-
- // If the caller has asked not to resume at this point, we make note
- // of this in the record so that we can skip it when trying to find
- // the top running activity.
- if (!doResume) {
- r.delayedResume = true;
- }
-
- HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
- != 0 ? r : null;
-
- // If the onlyIfNeeded flag is set, then we can do this if the activity
- // being launched is the same as the one making the call... or, as
- // a special case, if we do not know the caller then we count the
- // current top activity as the caller.
- if (onlyIfNeeded) {
- HistoryRecord checkedCaller = sourceRecord;
- if (checkedCaller == null) {
- checkedCaller = topRunningNonDelayedActivityLocked(notTop);
- }
- if (!checkedCaller.realActivity.equals(r.realActivity)) {
- // Caller is not the same as launcher, so always needed.
- onlyIfNeeded = false;
- }
- }
-
- if (grantedUriPermissions != null && callingUid > 0) {
- for (int i=0; i<grantedUriPermissions.length; i++) {
- grantUriPermissionLocked(callingUid, r.packageName,
- grantedUriPermissions[i], grantedMode, r);
- }
- }
-
- grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- intent, r);
-
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
- + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // The original activity who is starting us is running as a single
- // instance... this new activity it is starting must go on its
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
-
- if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // For whatever reason this activity is being launched into a new
- // task... yet the caller has requested a result back. Well, that
- // is pretty messed up, so instead immediately send back a cancel
- // and let the new task continue launched as normal without a
- // dependency on its originator.
- Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- r.resultTo = null;
- }
-
- boolean addingToTask = false;
- if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // If bring to front is requested, and no result is requested, and
- // we can find a task that was started with this same
- // component, then instead of launching bring that one to the front.
- if (r.resultTo == null) {
- // See if there is a task to bring to the front. If this is
- // a SINGLE_INSTANCE activity, there can be one and only one
- // instance of it in the history, and it is always in its own
- // unique task, so we do a special search.
- HistoryRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
- ? findTaskLocked(intent, r.info)
- : findActivityLocked(intent, r.info);
- if (taskTop != null) {
- if (taskTop.task.intent == null) {
- // This task was started because of movement of
- // the activity based on affinity... now that we
- // are actually launching it, we can assign the
- // base intent.
- taskTop.task.setIntent(intent, r.info);
- }
- // If the target task is not in the front, then we need
- // to bring it to the front... except... well, with
- // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
- // to have the same behavior as if a new instance was
- // being started, which means not bringing it to the front
- // if the caller is not itself in the front.
- HistoryRecord curTop = topRunningNonDelayedActivityLocked(notTop);
- if (curTop.task != taskTop.task) {
- r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- boolean callerAtFront = sourceRecord == null
- || curTop.task == sourceRecord.task;
- if (callerAtFront) {
- // We really do want to push this one into the
- // user's face, right now.
- moveTaskToFrontLocked(taskTop.task, r);
- }
- }
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- taskTop = resetTaskIfNeededLocked(taskTop, r);
- }
- if (onlyIfNeeded) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it! And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return START_RETURN_INTENT_TO_CALLER;
- }
- if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // In this situation we want to remove all activities
- // from the task up to the one being started. In most
- // cases this means we are resetting the task to its
- // initial state.
- HistoryRecord top = performClearTaskLocked(
- taskTop.task.taskId, r, launchFlags, true);
- if (top != null) {
- if (top.frontOfTask) {
- // Activity aliases may mean we use different
- // intents for the top activity, so make sure
- // the task now has the identity of the new
- // intent.
- top.task.setIntent(r.intent, r.info);
- }
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- deliverNewIntentLocked(top, r.intent);
- } else {
- // A special case: we need to
- // start the activity because it is not currently
- // running, and the caller has asked to clear the
- // current task to have this activity at the top.
- addingToTask = true;
- // Now pretend like this activity is being started
- // by the top of its task, so it is put in the
- // right place.
- sourceRecord = taskTop;
- }
- } else if (r.realActivity.equals(taskTop.task.realActivity)) {
- // In this case the top activity on the task is the
- // same as the one being launched, so we take that
- // as a request to bring the task to the foreground.
- // If the top activity in the task is the root
- // activity, deliver this new intent to it if it
- // desires.
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- && taskTop.realActivity.equals(r.realActivity)) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
- if (taskTop.frontOfTask) {
- taskTop.task.setIntent(r.intent, r.info);
- }
- deliverNewIntentLocked(taskTop, r.intent);
- } else if (!r.intent.filterEquals(taskTop.task.intent)) {
- // In this case we are launching the root activity
- // of the task, but with a different intent. We
- // should start a new instance on top.
- addingToTask = true;
- sourceRecord = taskTop;
- }
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
- // In this case an activity is being launched in to an
- // existing task, without resetting that task. This
- // is typically the situation of launching an activity
- // from a notification or shortcut. We want to place
- // the new activity on top of the current task.
- addingToTask = true;
- sourceRecord = taskTop;
- } else if (!taskTop.task.rootWasReset) {
- // In this case we are launching in to an existing task
- // that has not yet been started from its front door.
- // The current task has been brought to the front.
- // Ideally, we'd probably like to place this new task
- // at the bottom of its stack, but that's a little hard
- // to do with the current organization of the code so
- // for now we'll just drop it.
- taskTop.task.setIntent(r.intent, r.info);
- }
- if (!addingToTask) {
- // We didn't do anything... but it was needed (a.k.a., client
- // don't use that intent!) And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return START_TASK_TO_FRONT;
- }
- }
- }
- }
-
- //String uri = r.intent.toURI();
- //Intent intent2 = new Intent(uri);
- //Slog.i(TAG, "Given intent: " + r.intent);
- //Slog.i(TAG, "URI is: " + uri);
- //Slog.i(TAG, "To intent: " + intent2);
-
- if (r.packageName != null) {
- // If the activity being launched is the same as the one currently
- // at the top, then we need to check if it should only be launched
- // once.
- HistoryRecord top = topRunningNonDelayedActivityLocked(notTop);
- if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity)) {
- if (top.app != null && top.app.thread != null) {
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- if (onlyIfNeeded) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it!
- return START_RETURN_INTENT_TO_CALLER;
- }
- deliverNewIntentLocked(top, r.intent);
- return START_DELIVERED_TO_TOP;
- }
- }
- }
- }
-
- } else {
- if (r.resultTo != null) {
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- }
- return START_CLASS_NOT_FOUND;
- }
-
- boolean newTask = false;
-
- // Should this be considered a new task?
- if (r.resultTo == null && !addingToTask
- && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // todo: should do better management of integers.
- mCurTask++;
- if (mCurTask <= 0) {
- mCurTask = 1;
- }
- r.task = new TaskRecord(mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new task " + r.task);
- newTask = true;
- addRecentTaskLocked(r.task);
-
- } else if (sourceRecord != null) {
- if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- // In this case, we are adding the activity to an existing
- // task, but the caller has asked to clear that task if the
- // activity is already running.
- HistoryRecord top = performClearTaskLocked(
- sourceRecord.task.taskId, r, launchFlags, true);
- if (top != null) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- deliverNewIntentLocked(top, r.intent);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return START_DELIVERED_TO_TOP;
- }
- } else if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
- // In this case, we are launching an activity in our own task
- // that may already be running somewhere in the history, and
- // we want to shuffle it to the front of the stack if so.
- int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
- if (where >= 0) {
- HistoryRecord top = moveActivityToFrontLocked(where);
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- deliverNewIntentLocked(top, r.intent);
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return START_DELIVERED_TO_TOP;
- }
- }
- // An existing activity is starting this new activity, so we want
- // to keep the new one in the same task as the one that is starting
- // it.
- r.task = sourceRecord.task;
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in existing task " + r.task);
-
- } else {
- // This not being started from an existing activity, and not part
- // of a new task... just put it in the top task, though these days
- // this case should never happen.
- final int N = mHistory.size();
- HistoryRecord prev =
- N > 0 ? (HistoryRecord)mHistory.get(N-1) : null;
- r.task = prev != null
- ? prev.task
- : new TaskRecord(mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new guessed " + r.task);
- }
- if (newTask) {
- EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
- }
- logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
- startActivityLocked(r, newTask, doResume);
- return START_SUCCESS;
- }
-
- void reportActivityLaunchedLocked(boolean timeout, HistoryRecord r,
- long thisTime, long totalTime) {
- for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
- WaitResult w = mWaitingActivityLaunched.get(i);
- w.timeout = timeout;
- if (r != null) {
- w.who = new ComponentName(r.info.packageName, r.info.name);
- }
- w.thisTime = thisTime;
- w.totalTime = totalTime;
- }
- notify();
- }
-
- void reportActivityVisibleLocked(HistoryRecord r) {
- for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
- WaitResult w = mWaitingActivityVisible.get(i);
- w.timeout = false;
- if (r != null) {
- w.who = new ComponentName(r.info.packageName, r.info.name);
- }
- w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
- w.thisTime = w.totalTime;
- }
- notify();
- }
-
- private final int startActivityMayWait(IApplicationThread caller,
- Intent intent, String resolvedType, Uri[] grantedUriPermissions,
- int grantedMode, IBinder resultTo,
- String resultWho, int requestCode, boolean onlyIfNeeded,
- boolean debug, WaitResult outResult, Configuration config) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- final boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
- // Collect information about the target of the Intent.
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- ActivityThread.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | STOCK_PM_FLAGS);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
-
- if (aInfo != null) {
- // Store the found target back into the intent, because now that
- // we have it we never want to do this again. For example, if the
- // user navigates back to this point in the history, we should
- // always restart the exact same activity.
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
-
- // Don't debug things in the system process
- if (debug) {
- if (!aInfo.processName.equals("system")) {
- setDebugApp(aInfo.processName, true, false);
- }
- }
- }
-
- synchronized (this) {
- int callingPid;
- int callingUid;
- if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
- } else {
- callingPid = callingUid = -1;
- }
-
- mConfigWillChange = config != null && mConfiguration.diff(config) != 0;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Starting activity when config will change = " + mConfigWillChange);
-
- final long origId = Binder.clearCallingIdentity();
-
- int res = startActivityLocked(caller, intent, resolvedType,
- grantedUriPermissions, grantedMode, aInfo,
- resultTo, resultWho, requestCode, callingPid, callingUid,
- onlyIfNeeded, componentSpecified);
-
- if (mConfigWillChange) {
- // If the caller also wants to switch to a new configuration,
- // do so now. This allows a clean switch, as we are waiting
- // for the current activity to pause (so we will not destroy
- // it), and have not yet started the next activity.
- enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
- "updateConfiguration()");
- mConfigWillChange = false;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Updating to new configuration after starting activity.");
- updateConfigurationLocked(config, null);
- }
-
- Binder.restoreCallingIdentity(origId);
-
- if (outResult != null) {
- outResult.result = res;
- if (res == IActivityManager.START_SUCCESS) {
- mWaitingActivityLaunched.add(outResult);
- do {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- } else if (res == IActivityManager.START_TASK_TO_FRONT) {
- HistoryRecord r = this.topRunningActivityLocked(null);
- if (r.nowVisible) {
- outResult.timeout = false;
- outResult.who = new ComponentName(r.info.packageName, r.info.name);
- outResult.totalTime = 0;
- outResult.thisTime = 0;
- } else {
- outResult.thisTime = SystemClock.uptimeMillis();
- mWaitingActivityVisible.add(outResult);
- do {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- }
- }
- }
-
- return res;
- }
- }
-
public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
- return startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, null);
}
@@ -3799,7 +2014,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
WaitResult res = new WaitResult();
- startActivityMayWait(caller, intent, resolvedType,
+ mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, res, null);
return res;
@@ -3810,7 +2025,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, config);
}
@@ -3834,8 +2049,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
- if (mResumedActivity != null
- && mResumedActivity.info.applicationInfo.uid ==
+ if (mMainStack.mResumedActivity != null
+ && mMainStack.mResumedActivity.info.applicationInfo.uid ==
Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
}
@@ -3853,11 +2068,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
synchronized (this) {
- int index = indexOfTokenLocked(callingActivity);
+ int index = mMainStack.indexOfTokenLocked(callingActivity);
if (index < 0) {
return false;
}
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r.app == null || r.app.thread == null) {
// The caller is not running... d'oh!
return false;
@@ -3871,7 +2086,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ActivityInfo aInfo = null;
try {
List<ResolveInfo> resolves =
- ActivityThread.getPackageManager().queryIntentActivities(
+ AppGlobals.getPackageManager().queryIntentActivities(
intent, r.resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
@@ -3915,7 +2130,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.finishing = true;
// Propagate reply information over to the new activity.
- final HistoryRecord resultTo = r.resultTo;
+ final ActivityRecord resultTo = r.resultTo;
final String resultWho = r.resultWho;
final int requestCode = r.requestCode;
r.resultTo = null;
@@ -3926,7 +2141,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
// XXX we are not dealing with propagating grantedUriPermissions...
// those are not yet exposed to user code, so there is no need.
- int res = startActivityLocked(r.app.thread, intent,
+ int res = mMainStack.startActivityLocked(r.app.thread, intent,
r.resolvedType, null, 0, aInfo, resultTo, resultWho,
requestCode, -1, r.launchedFromUid, false, false);
Binder.restoreCallingIdentity(origId);
@@ -3960,7 +2175,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
- ActivityThread.getPackageManager().resolveIntent(
+ AppGlobals.getPackageManager().resolveIntent(
intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
aInfo = rInfo != null ? rInfo.activityInfo : null;
@@ -3978,13 +2193,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
synchronized(this) {
- return startActivityLocked(null, intent, resolvedType,
+ return mMainStack.startActivityLocked(null, intent, resolvedType,
null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid,
onlyIfNeeded, componentSpecified);
}
}
- private final void addRecentTaskLocked(TaskRecord task) {
+ final void addRecentTaskLocked(TaskRecord task) {
// Remove any existing entries that are the same kind of task.
int N = mRecentTasks.size();
for (int i=0; i<N; i++) {
@@ -4010,11 +2225,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public void setRequestedOrientation(IBinder token,
int requestedOrientation) {
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
mWindowManager.setAppOrientation(r, requestedOrientation);
Configuration config = mWindowManager.updateOrientationFromAppTokens(
@@ -4023,7 +2238,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (config != null) {
r.frozenBeforeDestroy = true;
if (!updateConfigurationLocked(config, r)) {
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
}
Binder.restoreCallingIdentity(origId);
@@ -4032,250 +2247,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public int getRequestedOrientation(IBinder token) {
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return mWindowManager.getAppOrientation(r);
}
}
- private final void stopActivityLocked(HistoryRecord r) {
- if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
- || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
- if (!r.finishing) {
- requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
- "no-history");
- }
- } else if (r.app != null && r.app.thread != null) {
- if (mFocusedActivity == r) {
- setFocusedActivityLocked(topRunningActivityLocked(null));
- }
- r.resumeKeyDispatchingLocked();
- try {
- r.stopped = false;
- r.state = ActivityState.STOPPING;
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Stopping visible=" + r.visible + " for " + r);
- if (!r.visible) {
- mWindowManager.setAppVisibility(r, false);
- }
- r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
- } catch (Exception e) {
- // Maybe just ignore exceptions here... if the process
- // has crashed, our death notification will clean things
- // up.
- Slog.w(TAG, "Exception thrown during pause", e);
- // Just in case, assume it to be stopped.
- r.stopped = true;
- r.state = ActivityState.STOPPED;
- if (r.configDestroy) {
- destroyActivityLocked(r, true);
- }
- }
- }
- }
-
- /**
- * @return Returns true if the activity is being finished, false if for
- * some reason it is being left as-is.
- */
- private final boolean requestFinishActivityLocked(IBinder token, int resultCode,
- Intent resultData, String reason) {
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Finishing activity: token=" + token
- + ", result=" + resultCode + ", data=" + resultData);
-
- int index = indexOfTokenLocked(token);
- if (index < 0) {
- return false;
- }
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
-
- // Is this the last activity left?
- boolean lastActivity = true;
- for (int i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord p = (HistoryRecord)mHistory.get(i);
- if (!p.finishing && p != r) {
- lastActivity = false;
- break;
- }
- }
-
- // If this is the last activity, but it is the home activity, then
- // just don't finish it.
- if (lastActivity) {
- if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
- return false;
- }
- }
-
- finishActivityLocked(r, index, resultCode, resultData, reason);
- return true;
- }
-
- /**
- * @return Returns true if this activity has been removed from the history
- * list, or false if it is still in the list and will be removed later.
- */
- private final boolean finishActivityLocked(HistoryRecord r, int index,
- int resultCode, Intent resultData, String reason) {
- if (r.finishing) {
- Slog.w(TAG, "Duplicate finish request for " + r);
- return false;
- }
-
- r.finishing = true;
- EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName, reason);
- r.task.numActivities--;
- if (index < (mHistory.size()-1)) {
- HistoryRecord next = (HistoryRecord)mHistory.get(index+1);
- if (next.task == r.task) {
- if (r.frontOfTask) {
- // The next activity is now the front of the task.
- next.frontOfTask = true;
- }
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- // If the caller asked that this activity (and all above it)
- // be cleared when the task is reset, don't lose that information,
- // but propagate it up to the next activity.
- next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- }
- }
- }
-
- r.pauseKeyDispatchingLocked();
- if (mFocusedActivity == r) {
- setFocusedActivityLocked(topRunningActivityLocked(null));
- }
-
- // send the result
- HistoryRecord resultTo = r.resultTo;
- if (resultTo != null) {
- if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
- + " who=" + r.resultWho + " req=" + r.requestCode
- + " res=" + resultCode + " data=" + resultData);
- if (r.info.applicationInfo.uid > 0) {
- grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
- r.packageName, resultData, r);
- }
- resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
- resultData);
- r.resultTo = null;
- }
- else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
-
- // Make sure this HistoryRecord is not holding on to other resources,
- // because clients have remote IPC references to this object so we
- // can't assume that will go away and want to avoid circular IPC refs.
- r.results = null;
- r.pendingResults = null;
- r.newIntents = null;
- r.icicle = null;
-
- if (mPendingThumbnails.size() > 0) {
- // There are clients waiting to receive thumbnails so, in case
- // this is an activity that someone is waiting for, add it
- // to the pending list so we can correctly update the clients.
- mCancelledThumbnails.add(r);
- }
-
- if (mResumedActivity == r) {
- boolean endTask = index <= 0
- || ((HistoryRecord)mHistory.get(index-1)).task != r.task;
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare close transition: finishing " + r);
- mWindowManager.prepareAppTransition(endTask
- ? WindowManagerPolicy.TRANSIT_TASK_CLOSE
- : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
-
- // Tell window manager to prepare for this one to be removed.
- mWindowManager.setAppVisibility(r, false);
-
- if (mPausingActivity == null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
- startPausingLocked(false, false);
- }
-
- } else if (r.state != ActivityState.PAUSING) {
- // If the activity is PAUSING, we will complete the finish once
- // it is done pausing; else we can just directly finish it here.
- if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
- return finishCurrentActivityLocked(r, index,
- FINISH_AFTER_PAUSE) == null;
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
- }
-
- return false;
- }
-
- private static final int FINISH_IMMEDIATELY = 0;
- private static final int FINISH_AFTER_PAUSE = 1;
- private static final int FINISH_AFTER_VISIBLE = 2;
-
- private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r,
- int mode) {
- final int index = indexOfTokenLocked(r);
- if (index < 0) {
- return null;
- }
-
- return finishCurrentActivityLocked(r, index, mode);
- }
-
- private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r,
- int index, int mode) {
- // First things first: if this activity is currently visible,
- // and the resumed activity is not yet visible, then hold off on
- // finishing until the resumed one becomes visible.
- if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
- if (!mStoppingActivities.contains(r)) {
- mStoppingActivities.add(r);
- if (mStoppingActivities.size() > 3) {
- // If we already have a few activities waiting to stop,
- // then give up on things going idle and start clearing
- // them out.
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
- }
- r.state = ActivityState.STOPPING;
- updateOomAdjLocked();
- return r;
- }
-
- // make sure the record is cleaned out of other places.
- mStoppingActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
- if (mResumedActivity == r) {
- mResumedActivity = null;
- }
- final ActivityState prevState = r.state;
- r.state = ActivityState.FINISHING;
-
- if (mode == FINISH_IMMEDIATELY
- || prevState == ActivityState.STOPPED
- || prevState == ActivityState.INITIALIZING) {
- // If this activity is already stopped, we can just finish
- // it right now.
- return destroyActivityLocked(r, true) ? null : r;
- } else {
- // Need to go through the full pause cycle to get this
- // activity into the stopped state and then finish it.
- if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
- mFinishingActivities.add(r);
- resumeTopActivityLocked(null);
- }
- return r;
- }
-
/**
* This is the internal entry point for handling Activity.finish().
*
@@ -4294,7 +2274,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(this) {
if (mController != null) {
// Find the first activity that is not finishing.
- HistoryRecord next = topRunningActivityLocked(token, 0);
+ ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
@@ -4310,57 +2290,119 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
final long origId = Binder.clearCallingIdentity();
- boolean res = requestFinishActivityLocked(token, resultCode,
+ boolean res = mMainStack.requestFinishActivityLocked(token, resultCode,
resultData, "app-request");
Binder.restoreCallingIdentity(origId);
return res;
}
}
- void sendActivityResultLocked(int callingUid, HistoryRecord r,
- String resultWho, int requestCode, int resultCode, Intent data) {
-
- if (callingUid > 0) {
- grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- data, r);
+ public final void finishHeavyWeightApp() {
+ if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: finishHeavyWeightApp() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
-
- if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
- + " : who=" + resultWho + " req=" + requestCode
- + " res=" + resultCode + " data=" + data);
- if (mResumedActivity == r && r.app != null && r.app.thread != null) {
- try {
- ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
- list.add(new ResultInfo(resultWho, requestCode,
- resultCode, data));
- r.app.thread.scheduleSendResult(r, list);
+
+ synchronized(this) {
+ if (mHeavyWeightProcess == null) {
return;
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending result to " + r, e);
}
+
+ ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(
+ mHeavyWeightProcess.activities);
+ for (int i=0; i<activities.size(); i++) {
+ ActivityRecord r = activities.get(i);
+ if (!r.finishing) {
+ int index = mMainStack.indexOfTokenLocked(r);
+ if (index >= 0) {
+ mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
+ null, "finish-heavy");
+ }
+ }
+ }
+
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
-
- r.addResultLocked(null, resultWho, requestCode, resultCode, data);
}
-
+
+ public void crashApplication(int uid, int initialPid, String packageName,
+ String message) {
+ if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: crashApplication() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ synchronized(this) {
+ ProcessRecord proc = null;
+
+ // Figure out which process to kill. We don't trust that initialPid
+ // still has any relation to current pids, so must scan through the
+ // list.
+ synchronized (mPidsSelfLocked) {
+ for (int i=0; i<mPidsSelfLocked.size(); i++) {
+ ProcessRecord p = mPidsSelfLocked.valueAt(i);
+ if (p.info.uid != uid) {
+ continue;
+ }
+ if (p.pid == initialPid) {
+ proc = p;
+ break;
+ }
+ for (String str : p.pkgList) {
+ if (str.equals(packageName)) {
+ proc = p;
+ }
+ }
+ }
+ }
+
+ if (proc == null) {
+ Slog.w(TAG, "crashApplication: nothing for uid=" + uid
+ + " initialPid=" + initialPid
+ + " packageName=" + packageName);
+ return;
+ }
+
+ if (proc.thread != null) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ proc.thread.scheduleCrash(message);
+ } catch (RemoteException e) {
+ }
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
public final void finishSubActivity(IBinder token, String resultWho,
int requestCode) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- HistoryRecord self = (HistoryRecord)mHistory.get(index);
+ ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
int i;
- for (i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.resultTo == self && r.requestCode == requestCode) {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
- finishActivityLocked(r, i,
+ mMainStack.finishActivityLocked(r, i,
Activity.RESULT_CANCELED, null, "request-sub");
}
}
@@ -4373,8 +2415,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public boolean willActivityBeVisible(IBinder token) {
synchronized(this) {
int i;
- for (i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r == token) {
return true;
}
@@ -4389,11 +2431,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public void overridePendingTransition(IBinder token, String packageName,
int enterAnim, int exitAnim) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- HistoryRecord self = (HistoryRecord)mHistory.get(index);
+ ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
@@ -4408,188 +2450,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
/**
- * Perform clean-up of service connections in an activity record.
- */
- private final void cleanUpActivityServicesLocked(HistoryRecord r) {
- // Throw away any services that have been bound by this activity.
- if (r.connections != null) {
- Iterator<ConnectionRecord> it = r.connections.iterator();
- while (it.hasNext()) {
- ConnectionRecord c = it.next();
- removeConnectionLocked(c, null, r);
- }
- r.connections = null;
- }
- }
-
- /**
- * Perform the common clean-up of an activity record. This is called both
- * as part of destroyActivityLocked() (when destroying the client-side
- * representation) and cleaning things up as a result of its hosting
- * processing going away, in which case there is no remaining client-side
- * state to destroy so only the cleanup here is needed.
- */
- private final void cleanUpActivityLocked(HistoryRecord r, boolean cleanServices) {
- if (mResumedActivity == r) {
- mResumedActivity = null;
- }
- if (mFocusedActivity == r) {
- mFocusedActivity = null;
- }
-
- r.configDestroy = false;
- r.frozenBeforeDestroy = false;
-
- // Make sure this record is no longer in the pending finishes list.
- // This could happen, for example, if we are trimming activities
- // down to the max limit while they are still waiting to finish.
- mFinishingActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
-
- // Remove any pending results.
- if (r.finishing && r.pendingResults != null) {
- for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
- PendingIntentRecord rec = apr.get();
- if (rec != null) {
- cancelIntentSenderLocked(rec, false);
- }
- }
- r.pendingResults = null;
- }
-
- if (cleanServices) {
- cleanUpActivityServicesLocked(r);
- }
-
- if (mPendingThumbnails.size() > 0) {
- // There are clients waiting to receive thumbnails so, in case
- // this is an activity that someone is waiting for, add it
- // to the pending list so we can correctly update the clients.
- mCancelledThumbnails.add(r);
- }
-
- // Get rid of any pending idle timeouts.
- mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
- }
-
- private final void removeActivityFromHistoryLocked(HistoryRecord r) {
- if (r.state != ActivityState.DESTROYED) {
- mHistory.remove(r);
- r.inHistory = false;
- r.state = ActivityState.DESTROYED;
- mWindowManager.removeAppToken(r);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- cleanUpActivityServicesLocked(r);
- removeActivityUriPermissionsLocked(r);
- }
- }
-
- /**
- * Destroy the current CLIENT SIDE instance of an activity. This may be
- * called both when actually finishing an activity, or when performing
- * a configuration switch where we destroy the current client-side object
- * but then create a new client-side object for this same HistoryRecord.
- */
- private final boolean destroyActivityLocked(HistoryRecord r,
- boolean removeFromApp) {
- if (DEBUG_SWITCH) Slog.v(
- TAG, "Removing activity: token=" + r
- + ", app=" + (r.app != null ? r.app.processName : "(null)"));
- EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
-
- boolean removedFromHistory = false;
-
- cleanUpActivityLocked(r, false);
-
- final boolean hadApp = r.app != null;
-
- if (hadApp) {
- if (removeFromApp) {
- int idx = r.app.activities.indexOf(r);
- if (idx >= 0) {
- r.app.activities.remove(idx);
- }
- if (r.persistent) {
- decPersistentCountLocked(r.app);
- }
- if (r.app.activities.size() == 0) {
- // No longer have activities, so update location in
- // LRU list.
- updateLruProcessLocked(r.app, true, false);
- }
- }
-
- boolean skipDestroy = false;
-
- try {
- if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
- r.app.thread.scheduleDestroyActivity(r, r.finishing,
- r.configChangeFlags);
- } catch (Exception e) {
- // We can just ignore exceptions here... if the process
- // has crashed, our death notification will clean things
- // up.
- //Slog.w(TAG, "Exception thrown during finish", e);
- if (r.finishing) {
- removeActivityFromHistoryLocked(r);
- removedFromHistory = true;
- skipDestroy = true;
- }
- }
-
- r.app = null;
- r.nowVisible = false;
-
- if (r.finishing && !skipDestroy) {
- r.state = ActivityState.DESTROYING;
- Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
- msg.obj = r;
- mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
- } else {
- r.state = ActivityState.DESTROYED;
- }
- } else {
- // remove this record from the history.
- if (r.finishing) {
- removeActivityFromHistoryLocked(r);
- removedFromHistory = true;
- } else {
- r.state = ActivityState.DESTROYED;
- }
- }
-
- r.configChangeFlags = 0;
-
- if (!mLRUActivities.remove(r) && hadApp) {
- Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
- }
-
- return removedFromHistory;
- }
-
- private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) {
- int i = list.size();
- if (localLOGV) Slog.v(
- TAG, "Removing app " + app + " from list " + list
- + " with " + i + " entries");
- while (i > 0) {
- i--;
- HistoryRecord r = (HistoryRecord)list.get(i);
- if (localLOGV) Slog.v(
- TAG, "Record #" + i + " " + r + ": app=" + r.app);
- if (r.app == app) {
- if (localLOGV) Slog.v(TAG, "Removing this entry!");
- list.remove(i);
- }
- }
- }
-
- /**
* Main function for removing an existing process from the activity manager
* as a result of that process going away. Clears out all connections
* to the process.
@@ -4602,30 +2462,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// Just in case...
- if (mPausingActivity != null && mPausingActivity.app == app) {
- if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + mPausingActivity);
- mPausingActivity = null;
+ if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity);
+ mMainStack.mPausingActivity = null;
}
- if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
- mLastPausedActivity = null;
+ if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) {
+ mMainStack.mLastPausedActivity = null;
}
// Remove this application's activities from active lists.
- removeHistoryRecordsForAppLocked(mLRUActivities, app);
- removeHistoryRecordsForAppLocked(mStoppingActivities, app);
- removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
- removeHistoryRecordsForAppLocked(mFinishingActivities, app);
+ mMainStack.removeHistoryRecordsForAppLocked(app);
boolean atTop = true;
boolean hasVisibleActivities = false;
// Clean out the history list.
- int i = mHistory.size();
+ int i = mMainStack.mHistory.size();
if (localLOGV) Slog.v(
TAG, "Removing app " + app + " from history with " + i + " entries");
while (i > 0) {
i--;
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (localLOGV) Slog.v(
TAG, "Record #" + i + " " + r + ": app=" + r.app);
if (r.app == app) {
@@ -4633,14 +2490,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (localLOGV) Slog.v(
TAG, "Removing this entry! frozen=" + r.haveState
+ " finishing=" + r.finishing);
- mHistory.remove(i);
+ mMainStack.mHistory.remove(i);
r.inHistory = false;
mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
+ mWindowManager.validateAppTokens(mMainStack.mHistory);
}
- removeActivityUriPermissionsLocked(r);
+ r.removeUriPermissionsLocked();
} else {
// We have the current state for this activity, so
@@ -4657,7 +2514,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- cleanUpActivityLocked(r, true);
+ r.stack.cleanUpActivityLocked(r, true);
r.state = ActivityState.STOPPED;
}
atTop = false;
@@ -4674,14 +2531,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (!restarting) {
- if (!resumeTopActivityLocked(null)) {
+ if (!mMainStack.resumeTopActivityLocked(null)) {
// If there was nothing to resume, and we are not already
// restarting this process, but there is a visible activity that
// is hosted by the process... then make sure all visible
// activities are running, taking care of restarting this
// process.
if (hasVisibleActivities) {
- ensureActivitiesVisibleLocked(null, 0);
+ mMainStack.ensureActivitiesVisibleLocked(null, 0);
}
}
}
@@ -4700,7 +2557,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return -1;
}
- private final ProcessRecord getRecordForAppLocked(
+ final ProcessRecord getRecordForAppLocked(
IApplicationThread thread) {
if (thread == null) {
return null;
@@ -4710,11 +2567,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return appIndex >= 0 ? mLruProcesses.get(appIndex) : null;
}
- private final void appDiedLocked(ProcessRecord app, int pid,
+ final void appDiedLocked(ProcessRecord app, int pid,
IApplicationThread thread) {
mProcDeaths[0]++;
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ stats.noteProcessDiedLocked(app.info.uid, pid);
+ }
+
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
@@ -4752,8 +2614,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
(rec.lastLowMemory+GC_MIN_INTERVAL) <= now) {
// The low memory report is overriding any current
// state for a GC request. Make sure to do
- // visible/foreground processes first.
- if (rec.setAdj <= VISIBLE_APP_ADJ) {
+ // heavy/important/visible/foreground processes first.
+ if (rec.setAdj <= HEAVY_WEIGHT_APP_ADJ) {
rec.lastRequestedGc = 0;
} else {
rec.lastRequestedGc = rec.lastLowMemory;
@@ -4830,8 +2692,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return tracesFile;
}
- final void appNotResponding(ProcessRecord app, HistoryRecord activity,
- HistoryRecord parent, final String annotation) {
+ final void appNotResponding(ProcessRecord app, ActivityRecord activity,
+ ActivityRecord parent, final String annotation) {
ArrayList<Integer> pids = new ArrayList<Integer>(20);
synchronized (this) {
@@ -4950,8 +2812,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void decPersistentCountLocked(ProcessRecord app)
- {
+ final void decPersistentCountLocked(ProcessRecord app) {
app.persistentActivities--;
if (app.persistentActivities > 0) {
// Still more of 'em...
@@ -4979,11 +2840,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
ProcessRecord app = r.app;
if (localLOGV) Slog.v(
@@ -5035,7 +2896,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int pid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
int pkgUid = -1;
synchronized(this) {
try {
@@ -5092,7 +2953,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
long callingId = Binder.clearCallingIdentity();
try {
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
int pkgUid = -1;
synchronized(this) {
try {
@@ -5124,7 +2985,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
long callingId = Binder.clearCallingIdentity();
try {
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
int pkgUid = -1;
synchronized(this) {
try {
@@ -5194,10 +3055,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mWindowManager.closeSystemDialogs(reason);
- for (i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
- finishActivityLocked(r, i,
+ r.stack.finishActivityLocked(r, i,
Activity.RESULT_CANCELED, null, "close-sys");
}
}
@@ -5298,7 +3159,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (uid < 0) {
try {
- uid = ActivityThread.getPackageManager().getPackageUid(name);
+ uid = AppGlobals.getPackageManager().getPackageUid(name);
} catch (RemoteException e) {
}
}
@@ -5318,8 +3179,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean didSomething = killPackageProcessesLocked(name, uid, -100,
callerWillRestart, doit);
- for (i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.packageName.equals(name)) {
if (!doit) {
return true;
@@ -5330,7 +3191,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.app.removed = true;
}
r.app = null;
- finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall");
+ r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall");
}
}
@@ -5362,7 +3223,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ac.removePackage(name);
}
}
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
return didSomething;
@@ -5376,6 +3237,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ "/" + uid + ")");
mProcessNames.remove(name, uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
boolean needRestart = false;
if (app.pid > 0 && app.pid != MY_PID) {
int pid = app.pid;
@@ -5417,6 +3282,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.info.uid,
app.processName);
mProcessNames.remove(app.processName, app.info.uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
// Take care of any launching providers waiting for this process.
checkAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
@@ -5583,12 +3452,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean didSomething = false;
// See if the top visible activity is waiting to run in this process...
- HistoryRecord hr = topRunningActivityLocked(null);
+ ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
if (hr != null && normalMode) {
if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
- if (realStartActivityLocked(hr, app, true, true)) {
+ if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (Exception e) {
@@ -5597,7 +3466,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
badApp = true;
}
} else {
- ensureActivitiesVisibleLocked(hr, null, processName, 0);
+ mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
}
}
@@ -5681,195 +3550,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public final void activityIdle(IBinder token, Configuration config) {
final long origId = Binder.clearCallingIdentity();
- activityIdleInternal(token, false, config);
+ mMainStack.activityIdleInternal(token, false, config);
Binder.restoreCallingIdentity(origId);
}
- final ArrayList<HistoryRecord> processStoppingActivitiesLocked(
- boolean remove) {
- int N = mStoppingActivities.size();
- if (N <= 0) return null;
-
- ArrayList<HistoryRecord> stops = null;
-
- final boolean nowVisible = mResumedActivity != null
- && mResumedActivity.nowVisible
- && !mResumedActivity.waitingVisible;
- for (int i=0; i<N; i++) {
- HistoryRecord s = mStoppingActivities.get(i);
- if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
- + nowVisible + " waitingVisible=" + s.waitingVisible
- + " finishing=" + s.finishing);
- if (s.waitingVisible && nowVisible) {
- mWaitingVisibleActivities.remove(s);
- s.waitingVisible = false;
- if (s.finishing) {
- // If this activity is finishing, it is sitting on top of
- // everyone else but we now know it is no longer needed...
- // so get rid of it. Otherwise, we need to go through the
- // normal flow and hide it once we determine that it is
- // hidden by the activities in front of it.
- if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
- mWindowManager.setAppVisibility(s, false);
- }
- }
- if (!s.waitingVisible && remove) {
- if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
- if (stops == null) {
- stops = new ArrayList<HistoryRecord>();
- }
- stops.add(s);
- mStoppingActivities.remove(i);
- N--;
- i--;
- }
- }
-
- return stops;
- }
-
void enableScreenAfterBoot() {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
mWindowManager.enableScreenAfterBoot();
}
- final void activityIdleInternal(IBinder token, boolean fromTimeout,
- Configuration config) {
- if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
-
- ArrayList<HistoryRecord> stops = null;
- ArrayList<HistoryRecord> finishes = null;
- ArrayList<HistoryRecord> thumbnails = null;
- int NS = 0;
- int NF = 0;
- int NT = 0;
- IApplicationThread sendThumbnail = null;
- boolean booting = false;
- boolean enableScreen = false;
-
- synchronized (this) {
- if (token != null) {
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
- }
-
- // Get the activity record.
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
-
- if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
- }
-
- // This is a hack to semi-deal with a race condition
- // in the client where it can be constructed with a
- // newer configuration from when we asked it to launch.
- // We'll update with whatever configuration it now says
- // it used to launch.
- if (config != null) {
- r.configuration = config;
- }
-
- // No longer need to keep the device awake.
- if (mResumedActivity == r && mLaunchingActivity.isHeld()) {
- mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- mLaunchingActivity.release();
- }
-
- // We are now idle. If someone is waiting for a thumbnail from
- // us, we can now deliver.
- r.idle = true;
- scheduleAppGcsLocked();
- if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
- sendThumbnail = r.app.thread;
- r.thumbnailNeeded = false;
- }
-
- // If this activity is fullscreen, set up to hide those under it.
-
- if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
- ensureActivitiesVisibleLocked(null, 0);
-
- //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (!mBooted && !fromTimeout) {
- mBooted = true;
- enableScreen = true;
- }
-
- } else if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
- }
-
- // Atomically retrieve all of the other things to do.
- stops = processStoppingActivitiesLocked(true);
- NS = stops != null ? stops.size() : 0;
- if ((NF=mFinishingActivities.size()) > 0) {
- finishes = new ArrayList<HistoryRecord>(mFinishingActivities);
- mFinishingActivities.clear();
- }
- if ((NT=mCancelledThumbnails.size()) > 0) {
- thumbnails = new ArrayList<HistoryRecord>(mCancelledThumbnails);
- mCancelledThumbnails.clear();
- }
-
- booting = mBooting;
- mBooting = false;
- }
-
- int i;
-
- // Send thumbnail if requested.
- if (sendThumbnail != null) {
- try {
- sendThumbnail.requestThumbnail(token);
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
- sendPendingThumbnail(null, token, null, null, true);
- }
- }
-
- // Stop any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (i=0; i<NS; i++) {
- HistoryRecord r = (HistoryRecord)stops.get(i);
- synchronized (this) {
- if (r.finishing) {
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
- } else {
- stopActivityLocked(r);
- }
- }
- }
-
- // Finish any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (i=0; i<NF; i++) {
- HistoryRecord r = (HistoryRecord)finishes.get(i);
- synchronized (this) {
- destroyActivityLocked(r, true);
- }
- }
-
- // Report back to any thumbnail receivers.
- for (i=0; i<NT; i++) {
- HistoryRecord r = (HistoryRecord)thumbnails.get(i);
- sendPendingThumbnail(r, null, null, null, true);
- }
-
- if (booting) {
- finishBooting();
- }
-
- trimApplications();
- //dump();
- //mWindowManager.dump();
-
- if (enableScreen) {
- enableScreenAfterBoot();
- }
- }
-
final void finishBooting() {
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
@@ -5904,7 +3594,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+ // Start looking for apps that are abusing wake locks.
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
// Tell anyone interested that we are done booting!
+ SystemProperties.set("sys.boot_completed", "1");
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_BOOT_COMPLETED, null),
null, null, 0, null, null,
@@ -5940,60 +3634,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
final long origId = Binder.clearCallingIdentity();
- activityPaused(token, icicle, false);
+ mMainStack.activityPaused(token, icicle, false);
Binder.restoreCallingIdentity(origId);
}
- final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
- if (DEBUG_PAUSE) Slog.v(
- TAG, "Activity paused: token=" + token + ", icicle=" + icicle
- + ", timeout=" + timeout);
-
- HistoryRecord r = null;
-
- synchronized (this) {
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- r = (HistoryRecord)mHistory.get(index);
- if (!timeout) {
- r.icicle = icicle;
- r.haveState = true;
- }
- mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- if (mPausingActivity == r) {
- r.state = ActivityState.PAUSED;
- completePauseLocked();
- } else {
- EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
- System.identityHashCode(r), r.shortComponentName,
- mPausingActivity != null
- ? mPausingActivity.shortComponentName : "(none)");
- }
- }
- }
- }
-
public final void activityStopped(IBinder token, Bitmap thumbnail,
CharSequence description) {
if (localLOGV) Slog.v(
TAG, "Activity stopped: token=" + token);
- HistoryRecord r = null;
+ ActivityRecord r = null;
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- r = (HistoryRecord)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
r.thumbnail = thumbnail;
r.description = description;
r.stopped = true;
r.state = ActivityState.STOPPED;
if (!r.finishing) {
if (r.configDestroy) {
- destroyActivityLocked(r, true);
- resumeTopActivityLocked(null);
+ r.stack.destroyActivityLocked(r, true);
+ r.stack.resumeTopActivityLocked(null);
}
}
}
@@ -6010,39 +3675,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public final void activityDestroyed(IBinder token) {
if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token);
- synchronized (this) {
- mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
-
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
- if (r.state == ActivityState.DESTROYING) {
- final long origId = Binder.clearCallingIdentity();
- removeActivityFromHistoryLocked(r);
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
+ mMainStack.activityDestroyed(token);
}
public String getCallingPackage(IBinder token) {
synchronized (this) {
- HistoryRecord r = getCallingRecordLocked(token);
+ ActivityRecord r = getCallingRecordLocked(token);
return r != null && r.app != null ? r.info.packageName : null;
}
}
public ComponentName getCallingActivity(IBinder token) {
synchronized (this) {
- HistoryRecord r = getCallingRecordLocked(token);
+ ActivityRecord r = getCallingRecordLocked(token);
return r != null ? r.intent.getComponent() : null;
}
}
- private HistoryRecord getCallingRecordLocked(IBinder token) {
- int index = indexOfTokenLocked(token);
+ private ActivityRecord getCallingRecordLocked(IBinder token) {
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r != null) {
return r.resultTo;
}
@@ -6052,9 +3705,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public ComponentName getActivityClassForToken(IBinder token) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return r.intent.getComponent();
}
return null;
@@ -6063,9 +3716,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public String getPackageForToken(IBinder token) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return r.packageName;
}
return null;
@@ -6092,7 +3745,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
try {
if (callingUid != 0 && callingUid != Process.SYSTEM_UID &&
Process.supportsProcesses()) {
- int uid = ActivityThread.getPackageManager()
+ int uid = AppGlobals.getPackageManager()
.getPackageUid(packageName);
if (uid != Binder.getCallingUid()) {
String msg = "Permission Denial: getIntentSender() from pid="
@@ -6104,57 +3757,66 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
throw new SecurityException(msg);
}
}
+
+ return getIntentSenderLocked(type, packageName, callingUid,
+ token, resultWho, requestCode, intent, resolvedType, flags);
+
} catch (RemoteException e) {
throw new SecurityException(e);
}
- HistoryRecord activity = null;
- if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- int index = indexOfTokenLocked(token);
- if (index < 0) {
- return null;
- }
- activity = (HistoryRecord)mHistory.get(index);
- if (activity.finishing) {
- return null;
- }
+ }
+ }
+
+ IIntentSender getIntentSenderLocked(int type,
+ String packageName, int callingUid, IBinder token, String resultWho,
+ int requestCode, Intent intent, String resolvedType, int flags) {
+ ActivityRecord activity = null;
+ if (type == INTENT_SENDER_ACTIVITY_RESULT) {
+ int index = mMainStack.indexOfTokenLocked(token);
+ if (index < 0) {
+ return null;
}
+ activity = (ActivityRecord)mMainStack.mHistory.get(index);
+ if (activity.finishing) {
+ return null;
+ }
+ }
- final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
- final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
- final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
- flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
- |PendingIntent.FLAG_UPDATE_CURRENT);
+ final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
+ final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
+ final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
+ flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
+ |PendingIntent.FLAG_UPDATE_CURRENT);
- PendingIntentRecord.Key key = new PendingIntentRecord.Key(
- type, packageName, activity, resultWho,
- requestCode, intent, resolvedType, flags);
- WeakReference<PendingIntentRecord> ref;
- ref = mIntentSenderRecords.get(key);
- PendingIntentRecord rec = ref != null ? ref.get() : null;
- if (rec != null) {
- if (!cancelCurrent) {
- if (updateCurrent) {
- rec.key.requestIntent.replaceExtras(intent);
- }
- return rec;
+ PendingIntentRecord.Key key = new PendingIntentRecord.Key(
+ type, packageName, activity, resultWho,
+ requestCode, intent, resolvedType, flags);
+ WeakReference<PendingIntentRecord> ref;
+ ref = mIntentSenderRecords.get(key);
+ PendingIntentRecord rec = ref != null ? ref.get() : null;
+ if (rec != null) {
+ if (!cancelCurrent) {
+ if (updateCurrent) {
+ rec.key.requestIntent.replaceExtras(intent);
}
- rec.canceled = true;
- mIntentSenderRecords.remove(key);
- }
- if (noCreate) {
return rec;
}
- rec = new PendingIntentRecord(this, key, callingUid);
- mIntentSenderRecords.put(key, rec.ref);
- if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- if (activity.pendingResults == null) {
- activity.pendingResults
- = new HashSet<WeakReference<PendingIntentRecord>>();
- }
- activity.pendingResults.add(rec.ref);
- }
+ rec.canceled = true;
+ mIntentSenderRecords.remove(key);
+ }
+ if (noCreate) {
return rec;
}
+ rec = new PendingIntentRecord(this, key, callingUid);
+ mIntentSenderRecords.put(key, rec.ref);
+ if (type == INTENT_SENDER_ACTIVITY_RESULT) {
+ if (activity.pendingResults == null) {
+ activity.pendingResults
+ = new HashSet<WeakReference<PendingIntentRecord>>();
+ }
+ activity.pendingResults.add(rec.ref);
+ }
+ return rec;
}
public void cancelIntentSender(IIntentSender sender) {
@@ -6164,7 +3826,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(this) {
PendingIntentRecord rec = (PendingIntentRecord)sender;
try {
- int uid = ActivityThread.getPackageManager()
+ int uid = AppGlobals.getPackageManager()
.getPackageUid(rec.key.packageName);
if (uid != Binder.getCallingUid()) {
String msg = "Permission Denial: cancelIntentSender() from pid="
@@ -6323,7 +3985,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return PackageManager.PERMISSION_GRANTED;
}
try {
- return ActivityThread.getPackageManager()
+ return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
// Should never happen, but if it does... deny!
@@ -6392,6 +4054,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return false;
}
}
+ if (!pi.exported && pi.applicationInfo.uid != uid) {
+ return false;
+ }
return true;
} catch (RemoteException e) {
return false;
@@ -6431,30 +4096,36 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private void grantUriPermissionLocked(int callingUid,
- String targetPkg, Uri uri, int modeFlags, HistoryRecord activity) {
+ /**
+ * Check if the targetPkg can be granted permission to access uri by
+ * the callingUid using the given modeFlags. Throws a security exception
+ * if callingUid is not allowed to do this. Returns the uid of the target
+ * if the URI permission grant should be performed; returns -1 if it is not
+ * needed (for example targetPkg already has permission to access the URI).
+ */
+ int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
+ Uri uri, int modeFlags) {
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
- return;
+ return -1;
}
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Requested grant " + targetPkg + " permission to " + uri);
+ "Checking grant " + targetPkg + " permission to " + uri);
- final IPackageManager pm = ActivityThread.getPackageManager();
+ final IPackageManager pm = AppGlobals.getPackageManager();
// If this is not a content: uri, we can't do anything with it.
if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Can't grant URI permission for non-content URI: " + uri);
- return;
+ return -1;
}
String name = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6466,7 +4137,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (pi == null) {
Slog.w(TAG, "No content provider found for: " + name);
- return;
+ return -1;
}
int targetUid;
@@ -6475,10 +4146,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (targetUid < 0) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Can't grant URI permission no uid for: " + targetPkg);
- return;
+ return -1;
}
} catch (RemoteException ex) {
- return;
+ return -1;
}
// First... does the target actually need this permission?
@@ -6486,7 +4157,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// No need to grant the target this permission.
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Target " + targetPkg + " already has full permission to " + uri);
- return;
+ return -1;
}
// Second... is the provider allowing granting of URI permissions?
@@ -6523,12 +4194,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- // Okay! So here we are: the caller has the assumed permission
+ return targetUid;
+ }
+
+ void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
+ Uri uri, int modeFlags, UriPermissionOwner owner) {
+ modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (modeFlags == 0) {
+ return;
+ }
+
+ // So here we are: the caller has the assumed permission
// to the uri, and the target doesn't. Let's now give this to
// the target.
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Granting " + targetPkg + " permission to " + uri);
+ "Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
HashMap<Uri, UriPermission> targetUris
= mGrantedUriPermissions.get(targetUid);
@@ -6541,37 +4223,68 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (perm == null) {
perm = new UriPermission(targetUid, uri);
targetUris.put(uri, perm);
-
}
+
perm.modeFlags |= modeFlags;
- if (activity == null) {
+ if (owner == null) {
perm.globalModeFlags |= modeFlags;
} else if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
- perm.readActivities.add(activity);
- if (activity.readUriPermissions == null) {
- activity.readUriPermissions = new HashSet<UriPermission>();
- }
- activity.readUriPermissions.add(perm);
+ perm.readOwners.add(owner);
+ owner.addReadPermission(perm);
} else if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
- perm.writeActivities.add(activity);
- if (activity.writeUriPermissions == null) {
- activity.writeUriPermissions = new HashSet<UriPermission>();
- }
- activity.writeUriPermissions.add(perm);
+ perm.writeOwners.add(owner);
+ owner.addWritePermission(perm);
}
}
- private void grantUriPermissionFromIntentLocked(int callingUid,
- String targetPkg, Intent intent, HistoryRecord activity) {
- if (intent == null) {
+ void grantUriPermissionLocked(int callingUid,
+ String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+ int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
+ if (targetUid < 0) {
return;
}
+
+ grantUriPermissionUncheckedLocked(targetUid, targetPkg, uri, modeFlags, owner);
+ }
+
+ /**
+ * Like checkGrantUriPermissionLocked, but takes an Intent.
+ */
+ int checkGrantUriPermissionFromIntentLocked(int callingUid,
+ String targetPkg, Intent intent) {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+ "Checking URI perm to " + (intent != null ? intent.getData() : null)
+ + " from " + intent + "; flags=0x"
+ + Integer.toHexString(intent != null ? intent.getFlags() : 0));
+
+ if (intent == null) {
+ return -1;
+ }
Uri data = intent.getData();
if (data == null) {
+ return -1;
+ }
+ return checkGrantUriPermissionLocked(callingUid, targetPkg, data,
+ intent.getFlags());
+ }
+
+ /**
+ * Like grantUriPermissionUncheckedLocked, but takes an Intent.
+ */
+ void grantUriPermissionUncheckedFromIntentLocked(int targetUid,
+ String targetPkg, Intent intent, UriPermissionOwner owner) {
+ grantUriPermissionUncheckedLocked(targetUid, targetPkg, intent.getData(),
+ intent.getFlags(), owner);
+ }
+
+ void grantUriPermissionFromIntentLocked(int callingUid,
+ String targetPkg, Intent intent, UriPermissionOwner owner) {
+ int targetUid = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg, intent);
+ if (targetUid < 0) {
return;
}
- grantUriPermissionLocked(callingUid, targetPkg, data,
- intent.getFlags(), activity);
+
+ grantUriPermissionUncheckedFromIntentLocked(targetUid, targetPkg, intent, owner);
}
public void grantUriPermission(IApplicationThread caller, String targetPkg,
@@ -6584,12 +4297,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ " when granting permission to uri " + uri);
}
if (targetPkg == null) {
- Slog.w(TAG, "grantUriPermission: null target");
- return;
+ throw new IllegalArgumentException("null target");
}
if (uri == null) {
- Slog.w(TAG, "grantUriPermission: null uri");
- return;
+ throw new IllegalArgumentException("null uri");
}
grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags,
@@ -6597,7 +4308,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private void removeUriPermissionIfNeededLocked(UriPermission perm) {
+ void removeUriPermissionIfNeededLocked(UriPermission perm) {
if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION
|Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) {
HashMap<Uri, UriPermission> perms
@@ -6613,29 +4324,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private void removeActivityUriPermissionsLocked(HistoryRecord activity) {
- if (activity.readUriPermissions != null) {
- for (UriPermission perm : activity.readUriPermissions) {
- perm.readActivities.remove(activity);
- if (perm.readActivities.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- removeUriPermissionIfNeededLocked(perm);
- }
- }
- }
- if (activity.writeUriPermissions != null) {
- for (UriPermission perm : activity.writeUriPermissions) {
- perm.writeActivities.remove(activity);
- if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- removeUriPermissionIfNeededLocked(perm);
- }
- }
- }
- }
-
private void revokeUriPermissionLocked(int callingUid, Uri uri,
int modeFlags) {
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -6647,12 +4335,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Revoking all granted permissions to " + uri);
- final IPackageManager pm = ActivityThread.getPackageManager();
+ final IPackageManager pm = AppGlobals.getPackageManager();
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProvidersByName.get(authority);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6742,12 +4429,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return;
}
- final IPackageManager pm = ActivityThread.getPackageManager();
+ final IPackageManager pm = AppGlobals.getPackageManager();
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProvidersByName.get(authority);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6766,6 +4452,56 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ @Override
+ public IBinder newUriPermissionOwner(String name) {
+ synchronized(this) {
+ UriPermissionOwner owner = new UriPermissionOwner(this, name);
+ return owner.getExternalTokenLocked();
+ }
+ }
+
+ @Override
+ public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
+ Uri uri, int modeFlags) {
+ synchronized(this) {
+ UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+ if (owner == null) {
+ throw new IllegalArgumentException("Unknown owner: " + token);
+ }
+ if (fromUid != Binder.getCallingUid()) {
+ if (Binder.getCallingUid() != Process.myUid()) {
+ // Only system code can grant URI permissions on behalf
+ // of other users.
+ throw new SecurityException("nice try");
+ }
+ }
+ if (targetPkg == null) {
+ throw new IllegalArgumentException("null target");
+ }
+ if (uri == null) {
+ throw new IllegalArgumentException("null uri");
+ }
+
+ grantUriPermissionLocked(fromUid, targetPkg, uri, modeFlags, owner);
+ }
+ }
+
+ @Override
+ public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode) {
+ synchronized(this) {
+ UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+ if (owner == null) {
+ throw new IllegalArgumentException("Unknown owner: " + token);
+ }
+
+ if (uri == null) {
+ owner.removeUriPermissionsLocked(mode);
+ } else {
+ owner.removeUriPermissionLocked(uri, mode);
+ }
+ }
+ }
+
public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
synchronized (this) {
ProcessRecord app =
@@ -6797,7 +4533,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
PendingThumbnailsRecord pending = null;
IApplicationThread topThumbnail = null;
- HistoryRecord topRecord = null;
+ ActivityRecord topRecord = null;
synchronized(this) {
if (localLOGV) Slog.v(
@@ -6822,18 +4558,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
throw new SecurityException(msg);
}
- int pos = mHistory.size()-1;
- HistoryRecord next =
- pos >= 0 ? (HistoryRecord)mHistory.get(pos) : null;
- HistoryRecord top = null;
+ int pos = mMainStack.mHistory.size()-1;
+ ActivityRecord next =
+ pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
+ ActivityRecord top = null;
CharSequence topDescription = null;
TaskRecord curTask = null;
int numActivities = 0;
int numRunning = 0;
while (pos >= 0 && maxNum > 0) {
- final HistoryRecord r = next;
+ final ActivityRecord r = next;
pos--;
- next = pos >= 0 ? (HistoryRecord)mHistory.get(pos) : null;
+ next = pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
// Initialize state for next task if needed.
if (top == null ||
@@ -6935,7 +4671,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
enforceCallingPermission(android.Manifest.permission.GET_TASKS,
"getRecentTasks()");
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
final int N = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res
@@ -6982,12 +4718,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
int j;
- TaskRecord startTask = ((HistoryRecord)mHistory.get(startIndex)).task;
+ TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task;
TaskRecord jt = startTask;
// First look backwards
for (j=startIndex-1; j>=0; j--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(j);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
if (r.task != jt) {
jt = r.task;
if (affinity.equals(jt.affinity)) {
@@ -6997,10 +4733,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// Now look forwards
- final int N = mHistory.size();
+ final int N = mMainStack.mHistory.size();
jt = startTask;
for (j=startIndex+1; j<N; j++) {
- HistoryRecord r = (HistoryRecord)mHistory.get(j);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
if (r.task != jt) {
if (affinity.equals(jt.affinity)) {
return j;
@@ -7010,7 +4746,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// Might it be at the top?
- if (affinity.equals(((HistoryRecord)mHistory.get(N-1)).task.affinity)) {
+ if (affinity.equals(((ActivityRecord)mMainStack.mHistory.get(N-1)).task.affinity)) {
return N-1;
}
@@ -7018,293 +4754,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
/**
- * Perform a reset of the given task, if needed as part of launching it.
- * Returns the new HistoryRecord at the top of the task.
- */
- private final HistoryRecord resetTaskIfNeededLocked(HistoryRecord taskTop,
- HistoryRecord newActivity) {
- boolean forceReset = (newActivity.info.flags
- &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
- if ((newActivity.info.flags
- &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
- forceReset = true;
- }
- }
-
- final TaskRecord task = taskTop.task;
-
- // We are going to move through the history list so that we can look
- // at each activity 'target' with 'below' either the interesting
- // activity immediately below it in the stack or null.
- HistoryRecord target = null;
- int targetI = 0;
- int taskTopI = -1;
- int replyChainEnd = -1;
- int lastReparentPos = -1;
- for (int i=mHistory.size()-1; i>=-1; i--) {
- HistoryRecord below = i >= 0 ? (HistoryRecord)mHistory.get(i) : null;
-
- if (below != null && below.finishing) {
- continue;
- }
- if (target == null) {
- target = below;
- targetI = i;
- // If we were in the middle of a reply chain before this
- // task, it doesn't appear like the root of the chain wants
- // anything interesting, so drop it.
- replyChainEnd = -1;
- continue;
- }
-
- final int flags = target.info.flags;
-
- final boolean finishOnTaskLaunch =
- (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
- final boolean allowTaskReparenting =
- (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
-
- if (target.task == task) {
- // We are inside of the task being reset... we'll either
- // finish this activity, push it out for another task,
- // or leave it as-is. We only do this
- // for activities that are not the root of the task (since
- // if we finish the root, we may no longer have the task!).
- if (taskTopI < 0) {
- taskTopI = targetI;
- }
- if (below != null && below.task == task) {
- final boolean clearWhenTaskReset =
- (target.intent.getFlags()
- &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
- if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) {
- // If this activity is sending a reply to a previous
- // activity, we can't do anything with it now until
- // we reach the start of the reply chain.
- // XXX note that we are assuming the result is always
- // to the previous activity, which is almost always
- // the case but we really shouldn't count on.
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting
- && target.taskAffinity != null
- && !target.taskAffinity.equals(task.affinity)) {
- // If this activity has an affinity for another
- // task, then we need to move it out of here. We will
- // move it as far out of the way as possible, to the
- // bottom of the activity stack. This also keeps it
- // correctly ordered with any activities we previously
- // moved.
- HistoryRecord p = (HistoryRecord)mHistory.get(0);
- if (target.taskAffinity != null
- && target.taskAffinity.equals(p.task.affinity)) {
- // If the activity currently at the bottom has the
- // same task affinity as the one we are moving,
- // then merge it into the same task.
- target.task = p.task;
- if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
- + " out to bottom task " + p.task);
- } else {
- mCurTask++;
- if (mCurTask <= 0) {
- mCurTask = 1;
- }
- target.task = new TaskRecord(mCurTask, target.info, null,
- (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- target.task.affinityIntent = target.intent;
- if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
- + " out to new task " + target.task);
- }
- mWindowManager.setAppGroupId(target, task.taskId);
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- int dstPos = 0;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (HistoryRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
- + " out to target's task " + target.task);
- task.numActivities--;
- p.task = target.task;
- target.task.numActivities++;
- mHistory.remove(srcPos);
- mHistory.add(dstPos, p);
- mWindowManager.moveAppToken(dstPos, p);
- mWindowManager.setAppGroupId(p, p.task.taskId);
- dstPos++;
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- i++;
- }
- if (taskTop == p) {
- taskTop = below;
- }
- if (taskTopI == replyChainEnd) {
- taskTopI = -1;
- }
- replyChainEnd = -1;
- addRecentTaskLocked(target.task);
- } else if (forceReset || finishOnTaskLaunch
- || clearWhenTaskReset) {
- // If the activity should just be removed -- either
- // because it asks for it, or the task should be
- // cleared -- then finish it and anything that is
- // part of its reply chain.
- if (clearWhenTaskReset) {
- // In this case, we want to finish this activity
- // and everything above it, so be sneaky and pretend
- // like these are all in the reply chain.
- replyChainEnd = targetI+1;
- while (replyChainEnd < mHistory.size() &&
- ((HistoryRecord)mHistory.get(
- replyChainEnd)).task == task) {
- replyChainEnd++;
- }
- replyChainEnd--;
- } else if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- HistoryRecord p = null;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (HistoryRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset")) {
- replyChainEnd--;
- srcPos--;
- }
- }
- if (taskTop == p) {
- taskTop = below;
- }
- if (taskTopI == replyChainEnd) {
- taskTopI = -1;
- }
- replyChainEnd = -1;
- } else {
- // If we were in the middle of a chain, well the
- // activity that started it all doesn't want anything
- // special, so leave it all as-is.
- replyChainEnd = -1;
- }
- } else {
- // Reached the bottom of the task -- any reply chain
- // should be left as-is.
- replyChainEnd = -1;
- }
-
- } else if (target.resultTo != null) {
- // If this activity is sending a reply to a previous
- // activity, we can't do anything with it now until
- // we reach the start of the reply chain.
- // XXX note that we are assuming the result is always
- // to the previous activity, which is almost always
- // the case but we really shouldn't count on.
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
-
- } else if (taskTopI >= 0 && allowTaskReparenting
- && task.affinity != null
- && task.affinity.equals(target.taskAffinity)) {
- // We are inside of another task... if this activity has
- // an affinity for our task, then either remove it if we are
- // clearing or move it over to our task. Note that
- // we currently punt on the case where we are resetting a
- // task that is not at the top but who has activities above
- // with an affinity to it... this is really not a normal
- // case, and we will need to later pull that task to the front
- // and usually at that point we will do the reset and pick
- // up those remaining activities. (This only happens if
- // someone starts an activity in a new task from an activity
- // in a task that is not currently on top.)
- if (forceReset || finishOnTaskLaunch) {
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- HistoryRecord p = null;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (HistoryRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset")) {
- taskTopI--;
- lastReparentPos--;
- replyChainEnd--;
- srcPos--;
- }
- }
- replyChainEnd = -1;
- } else {
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
- HistoryRecord p = (HistoryRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (lastReparentPos < 0) {
- lastReparentPos = taskTopI;
- taskTop = p;
- } else {
- lastReparentPos--;
- }
- mHistory.remove(srcPos);
- p.task.numActivities--;
- p.task = task;
- mHistory.add(lastReparentPos, p);
- if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
- + " in to resetting task " + task);
- task.numActivities++;
- mWindowManager.moveAppToken(lastReparentPos, p);
- mWindowManager.setAppGroupId(p, p.task.taskId);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- }
- replyChainEnd = -1;
-
- // Now we've moved it in to place... but what if this is
- // a singleTop activity and we have put it on top of another
- // instance of the same activity? Then we drop the instance
- // below so it remains singleTop.
- if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
- for (int j=lastReparentPos-1; j>=0; j--) {
- HistoryRecord p = (HistoryRecord)mHistory.get(j);
- if (p.finishing) {
- continue;
- }
- if (p.intent.getComponent().equals(target.intent.getComponent())) {
- if (finishActivityLocked(p, j,
- Activity.RESULT_CANCELED, null, "replace")) {
- taskTopI--;
- lastReparentPos--;
- }
- }
- }
- }
- }
- }
-
- target = below;
- targetI = i;
- }
-
- return taskTop;
- }
-
- /**
* TODO: Add mController hook
*/
public void moveTaskToFront(int task) {
@@ -7322,14 +4771,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (tr.taskId == task) {
- moveTaskToFrontLocked(tr, null);
+ mMainStack.moveTaskToFrontLocked(tr, null);
return;
}
}
- for (int i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord hr = (HistoryRecord)mHistory.get(i);
+ for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
if (hr.task.taskId == task) {
- moveTaskToFrontLocked(hr.task, null);
+ mMainStack.moveTaskToFrontLocked(hr.task, null);
return;
}
}
@@ -7339,84 +4788,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- private final void moveTaskToFrontLocked(TaskRecord tr, HistoryRecord reason) {
- if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
-
- final int task = tr.taskId;
- int top = mHistory.size()-1;
-
- if (top < 0 || ((HistoryRecord)mHistory.get(top)).task.taskId == task) {
- // nothing to do!
- return;
- }
-
- ArrayList moved = new ArrayList();
-
- // Applying the affinities may have removed entries from the history,
- // so get the size again.
- top = mHistory.size()-1;
- int pos = top;
-
- // Shift all activities with this task up to the top
- // of the stack, keeping them in the same internal order.
- while (pos >= 0) {
- HistoryRecord r = (HistoryRecord)mHistory.get(pos);
- if (localLOGV) Slog.v(
- TAG, "At " + pos + " ckp " + r.task + ": " + r);
- boolean first = true;
- if (r.task.taskId == task) {
- if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
- mHistory.remove(pos);
- mHistory.add(top, r);
- moved.add(0, r);
- top--;
- if (first) {
- addRecentTaskLocked(r.task);
- first = false;
- }
- }
- pos--;
- }
-
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare to front transition: task=" + tr);
- if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- HistoryRecord r = topRunningActivityLocked(null);
- if (r != null) {
- mNoAnimActivities.add(r);
- }
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
- }
-
- mWindowManager.moveAppTokensToTop(moved);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- finishTaskMoveLocked(task);
- EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task);
- }
-
- private final void finishTaskMoveLocked(int task) {
- resumeTopActivityLocked(null);
- }
-
public void moveTaskToBack(int task) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToBack()");
synchronized(this) {
- if (mResumedActivity != null && mResumedActivity.task.taskId == task) {
+ if (mMainStack.mResumedActivity != null
+ && mMainStack.mResumedActivity.task.taskId == task) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), "Task to back")) {
return;
}
}
final long origId = Binder.clearCallingIdentity();
- moveTaskToBackLocked(task, null);
+ mMainStack.moveTaskToBackLocked(task, null);
Binder.restoreCallingIdentity(origId);
}
}
@@ -7435,93 +4820,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
int taskId = getTaskForActivityLocked(token, !nonRoot);
if (taskId >= 0) {
- return moveTaskToBackLocked(taskId, null);
+ return mMainStack.moveTaskToBackLocked(taskId, null);
}
Binder.restoreCallingIdentity(origId);
}
return false;
}
- /**
- * Worker method for rearranging history stack. Implements the function of moving all
- * activities for a specific task (gathering them if disjoint) into a single group at the
- * bottom of the stack.
- *
- * If a watcher is installed, the action is preflighted and the watcher has an opportunity
- * to premeptively cancel the move.
- *
- * @param task The taskId to collect and move to the bottom.
- * @return Returns true if the move completed, false if not.
- */
- private final boolean moveTaskToBackLocked(int task, HistoryRecord reason) {
- Slog.i(TAG, "moveTaskToBack: " + task);
-
- // If we have a watcher, preflight the move before committing to it. First check
- // for *other* available tasks, but if none are available, then try again allowing the
- // current task to be selected.
- if (mController != null) {
- HistoryRecord next = topRunningActivityLocked(null, task);
- if (next == null) {
- next = topRunningActivityLocked(null, 0);
- }
- if (next != null) {
- // ask watcher if this is allowed
- boolean moveOK = true;
- try {
- moveOK = mController.activityResuming(next.packageName);
- } catch (RemoteException e) {
- mController = null;
- }
- if (!moveOK) {
- return false;
- }
- }
- }
-
- ArrayList moved = new ArrayList();
-
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare to back transition: task=" + task);
-
- final int N = mHistory.size();
- int bottom = 0;
- int pos = 0;
-
- // Shift all activities with this task down to the bottom
- // of the stack, keeping them in the same internal order.
- while (pos < N) {
- HistoryRecord r = (HistoryRecord)mHistory.get(pos);
- if (localLOGV) Slog.v(
- TAG, "At " + pos + " ckp " + r.task + ": " + r);
- if (r.task.taskId == task) {
- if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
- mHistory.remove(pos);
- mHistory.add(bottom, r);
- moved.add(r);
- bottom++;
- }
- pos++;
- }
-
- if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- HistoryRecord r = topRunningActivityLocked(null);
- if (r != null) {
- mNoAnimActivities.add(r);
- }
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
- }
- mWindowManager.moveAppTokensToBottom(moved);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- finishTaskMoveLocked(task);
- return true;
- }
-
public void moveTaskBackwards(int task) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskBackwards()");
@@ -7548,10 +4853,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
- final int N = mHistory.size();
+ final int N = mMainStack.mHistory.size();
TaskRecord lastTask = null;
for (int i=0; i<N; i++) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r == token) {
if (!onlyRoot || lastTask != r.task) {
return r.task.taskId;
@@ -7564,89 +4869,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return -1;
}
- /**
- * Returns the top activity in any existing task matching the given
- * Intent. Returns null if no such task is found.
- */
- private HistoryRecord findTaskLocked(Intent intent, ActivityInfo info) {
- ComponentName cls = intent.getComponent();
- if (info.targetActivity != null) {
- cls = new ComponentName(info.packageName, info.targetActivity);
- }
-
- TaskRecord cp = null;
-
- final int N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (!r.finishing && r.task != cp
- && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- cp = r.task;
- //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
- // + "/aff=" + r.task.affinity + " to new cls="
- // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
- if (r.task.affinity != null) {
- if (r.task.affinity.equals(info.taskAffinity)) {
- //Slog.i(TAG, "Found matching affinity!");
- return r;
- }
- } else if (r.task.intent != null
- && r.task.intent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
- return r;
- } else if (r.task.affinityIntent != null
- && r.task.affinityIntent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
- return r;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Returns the first activity (starting from the top of the stack) that
- * is the same as the given activity. Returns null if no such activity
- * is found.
- */
- private HistoryRecord findActivityLocked(Intent intent, ActivityInfo info) {
- ComponentName cls = intent.getComponent();
- if (info.targetActivity != null) {
- cls = new ComponentName(info.packageName, info.targetActivity);
- }
-
- final int N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
- if (!r.finishing) {
- if (r.intent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
- return r;
- }
- }
- }
-
- return null;
- }
-
public void finishOtherInstances(IBinder token, ComponentName className) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
- int N = mHistory.size();
+ int N = mMainStack.mHistory.size();
TaskRecord lastTask = null;
for (int i=0; i<N; i++) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.realActivity.equals(className)
&& r != token && lastTask != r.task) {
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
null, "others")) {
i--;
N--;
@@ -7671,7 +4904,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Binder.restoreCallingIdentity(origId);
}
- final void sendPendingThumbnail(HistoryRecord r, IBinder token,
+ final void sendPendingThumbnail(ActivityRecord r, IBinder token,
Bitmap thumbnail, CharSequence description, boolean always) {
TaskRecord task = null;
ArrayList receivers = null;
@@ -7680,11 +4913,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(this) {
if (r == null) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- r = (HistoryRecord)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
}
if (thumbnail == null) {
thumbnail = r.thumbnail;
@@ -7745,7 +4978,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private final List generateApplicationProvidersLocked(ProcessRecord app) {
List providers = null;
try {
- providers = ActivityThread.getPackageManager().
+ providers = AppGlobals.getPackageManager().
queryContentProviders(app.processName, app.info.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
@@ -7755,8 +4988,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
- ContentProviderRecord cpr =
- (ContentProviderRecord)mProvidersByClass.get(cpi.name);
+ ContentProviderRecord cpr = mProvidersByClass.get(cpi.name);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info);
mProvidersByClass.put(cpi.name, cpr);
@@ -7770,13 +5002,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
private final String checkContentProviderPermissionLocked(
- ProviderInfo cpi, ProcessRecord r, int mode) {
+ ProviderInfo cpi, ProcessRecord r) {
final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
cpi.exported ? -1 : cpi.applicationInfo.uid)
- == PackageManager.PERMISSION_GRANTED
- && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+ == PackageManager.PERMISSION_GRANTED) {
return null;
}
if (checkComponentPermission(cpi.writePermission, callingPid, callingUid,
@@ -7793,8 +5024,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
PathPermission pp = pps[i];
if (checkComponentPermission(pp.getReadPermission(), callingPid, callingUid,
cpi.exported ? -1 : cpi.applicationInfo.uid)
- == PackageManager.PERMISSION_GRANTED
- && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+ == PackageManager.PERMISSION_GRANTED) {
return null;
}
if (checkComponentPermission(pp.getWritePermission(), callingPid, callingUid,
@@ -7805,6 +5035,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+ if (perms != null) {
+ for (Map.Entry<Uri, UriPermission> uri : perms.entrySet()) {
+ if (uri.getKey().getAuthority().equals(cpi.authority)) {
+ return null;
+ }
+ }
+ }
+
String msg = "Permission Denial: opening provider " + cpi.name
+ " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
+ ", uid=" + callingUid + ") requires "
@@ -7831,13 +5070,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
// First check if this content provider has been published...
- cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ cpr = mProvidersByName.get(name);
if (cpr != null) {
cpi = cpr.info;
- if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
- return new ContentProviderHolder(cpi,
- cpi.readPermission != null
- ? cpi.readPermission : cpi.writePermission);
+ String msg;
+ if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+ throw new SecurityException(msg);
}
if (r != null && cpr.canRunHere(r)) {
@@ -7869,8 +5107,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
}
cpr.clients.add(r);
- if (cpr.app != null && r.setAdj >= VISIBLE_APP_ADJ) {
- // If this is a visible app accessing the provider,
+ if (cpr.app != null && r.setAdj <= PERCEPTIBLE_APP_ADJ) {
+ // If this is a perceptible app accessing the provider,
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
// content providers are often expensive to start.
@@ -7888,7 +5126,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else {
try {
- cpi = ActivityThread.getPackageManager().
+ cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
@@ -7897,10 +5135,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return null;
}
- if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
- return new ContentProviderHolder(cpi,
- cpi.readPermission != null
- ? cpi.readPermission : cpi.writePermission);
+ String msg;
+ if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+ throw new SecurityException(msg);
}
if (!mSystemReady && !mDidUpdate && !mWaitingUpdate
@@ -7912,12 +5149,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
"Attempt to launch content provider before system ready");
}
- cpr = (ContentProviderRecord)mProvidersByClass.get(cpi.name);
+ cpr = mProvidersByClass.get(cpi.name);
final boolean firstClass = cpr == null;
if (firstClass) {
try {
ApplicationInfo ai =
- ActivityThread.getPackageManager().
+ AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS);
@@ -8046,7 +5283,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
*/
public void removeContentProvider(IApplicationThread caller, String name) {
synchronized (this) {
- ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if(cpr == null) {
// remove from mProvidersByClass
if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -8060,8 +5297,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
" when removing content provider " + name);
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord)
- mProvidersByClass.get(cpr.info.name);
+ ContentProviderRecord localCpr = mProvidersByClass.get(cpr.info.name);
if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+ r.info.processName + " from process "
+ localCpr.appInfo.processName);
@@ -8085,7 +5321,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private void removeContentProviderExternal(String name) {
synchronized (this) {
- ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -8093,7 +5329,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord) mProvidersByClass.get(cpr.info.name);
+ ContentProviderRecord localCpr = mProvidersByClass.get(cpr.info.name);
localCpr.externals--;
if (localCpr.externals < 0) {
Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -8125,8 +5361,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (src == null || src.info == null || src.provider == null) {
continue;
}
- ContentProviderRecord dst =
- (ContentProviderRecord)r.pubProviders.get(src.info.name);
+ ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
mProvidersByClass.put(dst.info.name, dst);
String names[] = dst.info.authority.split(";");
@@ -8219,12 +5454,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
"unhandledBack()");
synchronized(this) {
- int count = mHistory.size();
+ int count = mMainStack.mHistory.size();
if (DEBUG_SWITCH) Slog.d(
TAG, "Performing unhandledBack(): stack size = " + count);
if (count > 1) {
final long origId = Binder.clearCallingIdentity();
- finishActivityLocked((HistoryRecord)mHistory.get(count-1),
+ mMainStack.finishActivityLocked((ActivityRecord)mMainStack.mHistory.get(count-1),
count-1, Activity.RESULT_CANCELED, null, "unhandled-back");
Binder.restoreCallingIdentity(origId);
}
@@ -8267,11 +5502,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mSleeping = true;
mWindowManager.setEventDispatching(false);
- if (mResumedActivity != null) {
- pauseIfSleepingLocked();
+ if (mMainStack.mResumedActivity != null) {
+ mMainStack.pauseIfSleepingLocked();
} else {
Slog.w(TAG, "goingToSleep with no resumed activity!");
}
+
+ // Initialize the wake times of all processes.
+ checkExcessiveWakeLocksLocked(false);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
}
}
@@ -8288,10 +5529,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mShuttingDown = true;
mWindowManager.setEventDispatching(false);
- if (mResumedActivity != null) {
- pauseIfSleepingLocked();
+ if (mMainStack.mResumedActivity != null) {
+ mMainStack.pauseIfSleepingLocked();
final long endTime = System.currentTimeMillis() + timeout;
- while (mResumedActivity != null || mPausingActivity != null) {
+ while (mMainStack.mResumedActivity != null
+ || mMainStack.mPausingActivity != null) {
long delay = endTime - System.currentTimeMillis();
if (delay <= 0) {
Slog.w(TAG, "Activity manager shutdown timed out");
@@ -8312,35 +5554,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return timedout;
}
- void pauseIfSleepingLocked() {
- if (mSleeping || mShuttingDown) {
- if (!mGoingToSleep.isHeld()) {
- mGoingToSleep.acquire();
- if (mLaunchingActivity.isHeld()) {
- mLaunchingActivity.release();
- mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- }
- }
-
- // If we are not currently pausing an activity, get the current
- // one to pause. If we are pausing one, we will just let that stuff
- // run and release the wake lock when all done.
- if (mPausingActivity == null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
- startPausingLocked(false, true);
- }
- }
- }
-
public void wakingUp() {
synchronized(this) {
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
+ if (mMainStack.mGoingToSleep.isHeld()) {
+ mMainStack.mGoingToSleep.release();
}
mWindowManager.setEventDispatching(true);
mSleeping = false;
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
}
}
@@ -8474,7 +5696,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// and started launching other packages.
if (!mSystemReady) {
try {
- ActivityThread.getPackageManager().enterSafeMode();
+ AppGlobals.getPackageManager().enterSafeMode();
} catch (RemoteException e) {
}
@@ -8560,123 +5782,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return killed;
}
- public void reportPss(IApplicationThread caller, int pss) {
- Watchdog.PssRequestor req;
- String name;
- ProcessRecord callerApp;
- synchronized (this) {
- if (caller == null) {
- return;
- }
- callerApp = getRecordForAppLocked(caller);
- if (callerApp == null) {
- return;
- }
- callerApp.lastPss = pss;
- req = callerApp;
- name = callerApp.processName;
- }
- Watchdog.getInstance().reportPss(req, name, pss);
- if (!callerApp.persistent) {
- removeRequestedPss(callerApp);
- }
- }
-
- public void requestPss(Runnable completeCallback) {
- ArrayList<ProcessRecord> procs;
- synchronized (this) {
- mRequestPssCallback = completeCallback;
- mRequestPssList.clear();
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord proc = mLruProcesses.get(i);
- if (!proc.persistent) {
- mRequestPssList.add(proc);
- }
- }
- procs = new ArrayList<ProcessRecord>(mRequestPssList);
- }
-
- int oldPri = Process.getThreadPriority(Process.myTid());
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- for (int i=procs.size()-1; i>=0; i--) {
- ProcessRecord proc = procs.get(i);
- proc.lastPss = 0;
- proc.requestPss();
- }
- Process.setThreadPriority(oldPri);
- }
-
- void removeRequestedPss(ProcessRecord proc) {
- Runnable callback = null;
- synchronized (this) {
- if (mRequestPssList.remove(proc)) {
- if (mRequestPssList.size() == 0) {
- callback = mRequestPssCallback;
- mRequestPssCallback = null;
- }
- }
- }
-
- if (callback != null) {
- callback.run();
- }
- }
-
- public void collectPss(Watchdog.PssStats stats) {
- stats.mEmptyPss = 0;
- stats.mEmptyCount = 0;
- stats.mBackgroundPss = 0;
- stats.mBackgroundCount = 0;
- stats.mServicePss = 0;
- stats.mServiceCount = 0;
- stats.mVisiblePss = 0;
- stats.mVisibleCount = 0;
- stats.mForegroundPss = 0;
- stats.mForegroundCount = 0;
- stats.mNoPssCount = 0;
- synchronized (this) {
- int i;
- int NPD = mProcDeaths.length < stats.mProcDeaths.length
- ? mProcDeaths.length : stats.mProcDeaths.length;
- int aggr = 0;
- for (i=0; i<NPD; i++) {
- aggr += mProcDeaths[i];
- stats.mProcDeaths[i] = aggr;
- }
- while (i<stats.mProcDeaths.length) {
- stats.mProcDeaths[i] = 0;
- i++;
- }
-
- for (i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord proc = mLruProcesses.get(i);
- if (proc.persistent) {
- continue;
- }
- //Slog.i(TAG, "Proc " + proc + ": pss=" + proc.lastPss);
- if (proc.lastPss == 0) {
- stats.mNoPssCount++;
- continue;
- }
- if (proc.setAdj >= HIDDEN_APP_MIN_ADJ) {
- if (proc.empty) {
- stats.mEmptyPss += proc.lastPss;
- stats.mEmptyCount++;
- } else {
- stats.mBackgroundPss += proc.lastPss;
- stats.mBackgroundCount++;
- }
- } else if (proc.setAdj >= VISIBLE_APP_ADJ) {
- stats.mVisiblePss += proc.lastPss;
- stats.mVisibleCount++;
- } else {
- stats.mForegroundPss += proc.lastPss;
- stats.mForegroundCount++;
- }
- }
- }
- }
-
public final void startRunning(String pkg, String cls, String action,
String data) {
synchronized(this) {
@@ -8747,7 +5852,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
List<ResolveInfo> ris = null;
try {
- ris = ActivityThread.getPackageManager().queryIntentReceivers(
+ ris = AppGlobals.getPackageManager().queryIntentReceivers(
intent, null, 0);
} catch (RemoteException e) {
}
@@ -8866,7 +5971,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
- List apps = ActivityThread.getPackageManager().
+ List apps = AppGlobals.getPackageManager().
getPersistentApplications(STOCK_PM_FLAGS);
if (apps != null) {
int N = apps.size();
@@ -8889,7 +5994,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mBooting = true;
try {
- if (ActivityThread.getPackageManager().hasSystemUidErrors()) {
+ if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
Message msg = Message.obtain();
msg.what = SHOW_UID_ERROR_MSG;
mHandler.sendMessage(msg);
@@ -8897,7 +6002,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} catch (RemoteException e) {
}
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
}
@@ -8985,12 +6090,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.info.processName, app.info.uid);
killServicesLocked(app, false);
- for (int i=mHistory.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.app == app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
+ r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
}
}
if (!app.persistent) {
@@ -9008,28 +6113,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return false;
}
} else {
- HistoryRecord r = topRunningActivityLocked(null);
+ ActivityRecord r = mMainStack.topRunningActivityLocked(null);
if (r.app == app) {
// If the top running activity is from this crashing
// process, then terminate it to avoid getting in a loop.
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- int index = indexOfTokenLocked(r);
- finishActivityLocked(r, index,
+ int index = mMainStack.indexOfTokenLocked(r);
+ r.stack.finishActivityLocked(r, index,
Activity.RESULT_CANCELED, null, "crashed");
// Also terminate an activities below it that aren't yet
// stopped, to avoid a situation where one will get
// re-start our crashing activity once it gets resumed again.
index--;
if (index >= 0) {
- r = (HistoryRecord)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r.state == ActivityState.RESUMED
|| r.state == ActivityState.PAUSING
|| r.state == ActivityState.PAUSED) {
if (!r.isHomeActivity) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishActivityLocked(r, index,
+ r.stack.finishActivityLocked(r, index,
Activity.RESULT_CANCELED, null, "crashed");
}
}
@@ -9041,9 +6146,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (app.services.size() != 0) {
// Any services running in the application need to be placed
// back in the pending list.
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord sr = (ServiceRecord)it.next();
+ ServiceRecord sr = it.next();
sr.crashCount++;
}
}
@@ -9056,7 +6161,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
&& (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
Iterator it = mHomeProcess.activities.iterator();
while (it.hasNext()) {
- HistoryRecord r = (HistoryRecord)it.next();
+ ActivityRecord r = (ActivityRecord)it.next();
if (r.isHomeActivity) {
Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
try {
@@ -9127,6 +6232,161 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
crashApplication(r, crashInfo);
}
+ public void handleApplicationStrictModeViolation(
+ IBinder app,
+ int violationMask,
+ StrictMode.ViolationInfo info) {
+ ProcessRecord r = findAppProcess(app);
+
+ if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
+ Integer stackFingerprint = info.crashInfo.stackTrace.hashCode();
+ boolean logIt = true;
+ synchronized (mAlreadyLoggedViolatedStacks) {
+ if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
+ logIt = false;
+ // TODO: sub-sample into EventLog for these, with
+ // the info.durationMillis? Then we'd get
+ // the relative pain numbers, without logging all
+ // the stack traces repeatedly. We'd want to do
+ // likewise in the client code, which also does
+ // dup suppression, before the Binder call.
+ } else {
+ if (mAlreadyLoggedViolatedStacks.size() >= MAX_DUP_SUPPRESSED_STACKS) {
+ mAlreadyLoggedViolatedStacks.clear();
+ }
+ mAlreadyLoggedViolatedStacks.add(stackFingerprint);
+ }
+ }
+ if (logIt) {
+ logStrictModeViolationToDropBox(r, info);
+ }
+ }
+
+ if ((violationMask & StrictMode.PENALTY_DIALOG) != 0) {
+ AppErrorResult result = new AppErrorResult();
+ synchronized (this) {
+ final long origId = Binder.clearCallingIdentity();
+
+ Message msg = Message.obtain();
+ msg.what = SHOW_STRICT_MODE_VIOLATION_MSG;
+ HashMap<String, Object> data = new HashMap<String, Object>();
+ data.put("result", result);
+ data.put("app", r);
+ data.put("violationMask", violationMask);
+ data.put("info", info);
+ msg.obj = data;
+ mHandler.sendMessage(msg);
+
+ Binder.restoreCallingIdentity(origId);
+ }
+ int res = result.get();
+ Slog.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+ }
+ }
+
+ // Depending on the policy in effect, there could be a bunch of
+ // these in quick succession so we try to batch these together to
+ // minimize disk writes, number of dropbox entries, and maximize
+ // compression, by having more fewer, larger records.
+ private void logStrictModeViolationToDropBox(
+ ProcessRecord process,
+ StrictMode.ViolationInfo info) {
+ if (info == null) {
+ return;
+ }
+ final boolean isSystemApp = process == null ||
+ (process.info.flags & (ApplicationInfo.FLAG_SYSTEM |
+ ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
+ final String dropboxTag = isSystemApp ? "system_app_strictmode" : "data_app_strictmode";
+ final DropBoxManager dbox = (DropBoxManager)
+ mContext.getSystemService(Context.DROPBOX_SERVICE);
+
+ // Exit early if the dropbox isn't configured to accept this report type.
+ if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
+
+ boolean bufferWasEmpty;
+ boolean needsFlush;
+ final StringBuilder sb = isSystemApp ? mStrictModeBuffer : new StringBuilder(1024);
+ synchronized (sb) {
+ bufferWasEmpty = sb.length() == 0;
+ appendDropBoxProcessHeaders(process, sb);
+ sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+ sb.append("System-App: ").append(isSystemApp).append("\n");
+ sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n");
+ if (info.violationNumThisLoop != 0) {
+ sb.append("Loop-Violation-Number: ").append(info.violationNumThisLoop).append("\n");
+ }
+ if (info != null && info.durationMillis != -1) {
+ sb.append("Duration-Millis: ").append(info.durationMillis).append("\n");
+ }
+ sb.append("\n");
+ if (info.crashInfo != null && info.crashInfo.stackTrace != null) {
+ sb.append(info.crashInfo.stackTrace);
+ }
+ sb.append("\n");
+
+ // Only buffer up to ~64k. Various logging bits truncate
+ // things at 128k.
+ needsFlush = (sb.length() > 64 * 1024);
+ }
+
+ // Flush immediately if the buffer's grown too large, or this
+ // is a non-system app. Non-system apps are isolated with a
+ // different tag & policy and not batched.
+ //
+ // Batching is useful during internal testing with
+ // StrictMode settings turned up high. Without batching,
+ // thousands of separate files could be created on boot.
+ if (!isSystemApp || needsFlush) {
+ new Thread("Error dump: " + dropboxTag) {
+ @Override
+ public void run() {
+ String report;
+ synchronized (sb) {
+ report = sb.toString();
+ sb.delete(0, sb.length());
+ sb.trimToSize();
+ }
+ if (report.length() != 0) {
+ dbox.addText(dropboxTag, report);
+ }
+ }
+ }.start();
+ return;
+ }
+
+ // System app batching:
+ if (!bufferWasEmpty) {
+ // An existing dropbox-writing thread is outstanding, so
+ // we don't need to start it up. The existing thread will
+ // catch the buffer appends we just did.
+ return;
+ }
+
+ // Worker thread to both batch writes and to avoid blocking the caller on I/O.
+ // (After this point, we shouldn't access AMS internal data structures.)
+ new Thread("Error dump: " + dropboxTag) {
+ @Override
+ public void run() {
+ // 5 second sleep to let stacks arrive and be batched together
+ try {
+ Thread.sleep(5000); // 5 seconds
+ } catch (InterruptedException e) {}
+
+ String errorReport;
+ synchronized (mStrictModeBuffer) {
+ errorReport = mStrictModeBuffer.toString();
+ if (errorReport.length() == 0) {
+ return;
+ }
+ mStrictModeBuffer.delete(0, mStrictModeBuffer.length());
+ mStrictModeBuffer.trimToSize();
+ }
+ dbox.addText(dropboxTag, errorReport);
+ }
+ }.start();
+ }
+
/**
* Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors.
* @param app object of the crashing app, null for the system server
@@ -9180,40 +6440,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
/**
- * Write a description of an error (crash, WTF, ANR) to the drop box.
- * @param eventType to include in the drop box tag ("crash", "wtf", etc.)
- * @param process which caused the error, null means the system server
- * @param activity which triggered the error, null if unknown
- * @param parent activity related to the error, null if unknown
- * @param subject line related to the error, null if absent
- * @param report in long form describing the error, null if absent
- * @param logFile to include in the report, null if none
- * @param crashInfo giving an application stack trace, null if absent
+ * Utility function for addErrorToDropBox and handleStrictModeViolation's logging
+ * to append various headers to the dropbox log text.
*/
- public void addErrorToDropBox(String eventType,
- ProcessRecord process, HistoryRecord activity, HistoryRecord parent, String subject,
- final String report, final File logFile,
- final ApplicationErrorReport.CrashInfo crashInfo) {
- // NOTE -- this must never acquire the ActivityManagerService lock,
- // otherwise the watchdog may be prevented from resetting the system.
-
- String prefix;
- if (process == null || process.pid == MY_PID) {
- prefix = "system_server_";
- } else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- prefix = "system_app_";
- } else {
- prefix = "data_app_";
- }
-
- final String dropboxTag = prefix + eventType;
- final DropBoxManager dbox = (DropBoxManager)
- mContext.getSystemService(Context.DROPBOX_SERVICE);
-
- // Exit early if the dropbox isn't configured to accept this report type.
- if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
-
- final StringBuilder sb = new StringBuilder(1024);
+ private static void appendDropBoxProcessHeaders(ProcessRecord process, StringBuilder sb) {
if (process == null || process.pid == MY_PID) {
sb.append("Process: system_server\n");
} else {
@@ -9221,7 +6451,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (process != null) {
int flags = process.info.flags;
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n");
for (String pkg : process.pkgList) {
sb.append("Package: ").append(pkg);
@@ -9239,6 +6469,45 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
sb.append("\n");
}
}
+ }
+
+ private static String processClass(ProcessRecord process) {
+ if (process == null || process.pid == MY_PID) {
+ return "system_server";
+ } else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return "system_app";
+ } else {
+ return "data_app";
+ }
+ }
+
+ /**
+ * Write a description of an error (crash, WTF, ANR) to the drop box.
+ * @param eventType to include in the drop box tag ("crash", "wtf", etc.)
+ * @param process which caused the error, null means the system server
+ * @param activity which triggered the error, null if unknown
+ * @param parent activity related to the error, null if unknown
+ * @param subject line related to the error, null if absent
+ * @param report in long form describing the error, null if absent
+ * @param logFile to include in the report, null if none
+ * @param crashInfo giving an application stack trace, null if absent
+ */
+ public void addErrorToDropBox(String eventType,
+ ProcessRecord process, ActivityRecord activity, ActivityRecord parent, String subject,
+ final String report, final File logFile,
+ final ApplicationErrorReport.CrashInfo crashInfo) {
+ // NOTE -- this must never acquire the ActivityManagerService lock,
+ // otherwise the watchdog may be prevented from resetting the system.
+
+ final String dropboxTag = processClass(process) + "_" + eventType;
+ final DropBoxManager dbox = (DropBoxManager)
+ mContext.getSystemService(Context.DROPBOX_SERVICE);
+
+ // Exit early if the dropbox isn't configured to accept this report type.
+ if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
+
+ final StringBuilder sb = new StringBuilder(1024);
+ appendDropBoxProcessHeaders(process, sb);
if (activity != null) {
sb.append("Activity: ").append(activity.shortComponentName).append("\n");
}
@@ -9496,6 +6765,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
new ActivityManager.RunningAppProcessInfo(app.processName,
app.pid, app.getPackageList());
currApp.uid = app.info.uid;
+ if (mHeavyWeightProcess == app) {
+ currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HEAVY_WEIGHT;
+ }
int adj = app.curAdj;
if (adj >= EMPTY_APP_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY;
@@ -9507,6 +6779,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
currApp.lru = 0;
} else if (adj >= SECONDARY_SERVER_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
+ } else if (adj >= HEAVY_WEIGHT_APP_ADJ) {
+ currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_HEAVY_WEIGHT;
+ } else if (adj >= PERCEPTIBLE_APP_ADJ) {
+ currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
} else if (adj >= VISIBLE_APP_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
} else {
@@ -9515,8 +6791,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
currApp.importanceReasonCode = app.adjTypeCode;
if (app.adjSource instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
- } else if (app.adjSource instanceof HistoryRecord) {
- HistoryRecord r = (HistoryRecord)app.adjSource;
+ } else if (app.adjSource instanceof ActivityRecord) {
+ ActivityRecord r = (ActivityRecord)app.adjSource;
if (r.app != null) currApp.importanceReasonPid = r.app.pid;
}
if (app.adjTarget instanceof ComponentName) {
@@ -9546,7 +6822,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
}
- IPackageManager pm = ActivityThread.getPackageManager();
+ IPackageManager pm = AppGlobals.getPackageManager();
for (String pkg : extList) {
try {
ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
@@ -9630,7 +6906,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
return;
} else if ("service".equals(cmd)) {
- dumpService(fd, pw, args, opti, true);
+ dumpService(fd, pw, args, opti, dumpAll);
return;
} else if ("services".equals(cmd) || "s".equals(cmd)) {
synchronized (this) {
@@ -9695,31 +6971,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (needHeader) {
pw.println(" Activity stack:");
}
- dumpHistoryList(pw, mHistory, " ", "Hist", true);
+ dumpHistoryList(pw, mMainStack.mHistory, " ", "Hist", true);
pw.println(" ");
pw.println(" Running activities (most recent first):");
- dumpHistoryList(pw, mLRUActivities, " ", "Run", false);
- if (mWaitingVisibleActivities.size() > 0) {
+ dumpHistoryList(pw, mMainStack.mLRUActivities, " ", "Run", false);
+ if (mMainStack.mWaitingVisibleActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting for another to become visible:");
- dumpHistoryList(pw, mWaitingVisibleActivities, " ", "Wait", false);
+ dumpHistoryList(pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false);
}
- if (mStoppingActivities.size() > 0) {
+ if (mMainStack.mStoppingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to stop:");
- dumpHistoryList(pw, mStoppingActivities, " ", "Stop", false);
+ dumpHistoryList(pw, mMainStack.mStoppingActivities, " ", "Stop", false);
}
- if (mFinishingActivities.size() > 0) {
+ if (mMainStack.mFinishingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to finish:");
- dumpHistoryList(pw, mFinishingActivities, " ", "Fin", false);
+ dumpHistoryList(pw, mMainStack.mFinishingActivities, " ", "Fin", false);
}
pw.println(" ");
- pw.println(" mPausingActivity: " + mPausingActivity);
- pw.println(" mResumedActivity: " + mResumedActivity);
+ pw.println(" mPausingActivity: " + mMainStack.mPausingActivity);
+ pw.println(" mResumedActivity: " + mMainStack.mResumedActivity);
pw.println(" mFocusedActivity: " + mFocusedActivity);
- pw.println(" mLastPausedActivity: " + mLastPausedActivity);
+ pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity);
if (dumpAll && mRecentTasks.size() > 0) {
pw.println(" ");
@@ -9770,7 +7046,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
needSep = true;
pw.println(" Running processes (most recent first):");
dumpProcessList(pw, this, mLruProcesses, " ",
- "App ", "PERS", true);
+ "Proc", "PERS", true);
needSep = true;
}
@@ -9884,8 +7160,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pw.println(" ");
pw.println(" mHomeProcess: " + mHomeProcess);
+ if (mHeavyWeightProcess != null) {
+ pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ }
pw.println(" mConfiguration: " + mConfiguration);
- pw.println(" mConfigWillChange: " + mConfigWillChange);
+ pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange);
pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
@@ -9904,8 +7183,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ " mBooting=" + mBooting
+ " mBooted=" + mBooted
+ " mFactoryTest=" + mFactoryTest);
- pw.println(" mGoingToSleep=" + mGoingToSleep);
- pw.println(" mLaunchingActivity=" + mLaunchingActivity);
+ pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep);
+ pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity);
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
}
@@ -9933,20 +7212,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
componentNameString = args[opti];
opti++;
ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
- r = componentName != null ? mServices.get(componentName) : null;
+ synchronized (this) {
+ r = componentName != null ? mServices.get(componentName) : null;
+ }
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
}
if (r != null) {
- dumpService(fd, pw, r, newArgs);
+ dumpService(fd, pw, r, newArgs, dumpAll);
} else {
- for (ServiceRecord r1 : mServices.values()) {
- if (componentNameString == null
- || r1.name.flattenToString().contains(componentNameString)) {
- dumpService(fd, pw, r1, newArgs);
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ synchronized (this) {
+ for (ServiceRecord r1 : mServices.values()) {
+ if (componentNameString == null
+ || r1.name.flattenToString().contains(componentNameString)) {
+ services.add(r1);
+ }
}
}
+ for (int i=0; i<services.size(); i++) {
+ dumpService(fd, pw, services.get(i), newArgs, dumpAll);
+ }
}
}
@@ -9954,8 +7241,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* Invokes IApplicationThread.dumpService() on the thread of the specified service if
* there is a thread associated with the service.
*/
- private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args) {
+ private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args,
+ boolean dumpAll) {
pw.println(" Service " + r.name.flattenToString());
+ if (dumpAll) {
+ synchronized (this) {
+ pw.print(" * "); pw.println(r);
+ r.dump(pw, " ");
+ }
+ pw.println("");
+ }
if (r.app != null && r.app.thread != null) {
try {
// flush anything that is already in the PrintWriter since the thread is going
@@ -9963,6 +7258,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
pw.flush();
r.app.thread.dumpService(fd, r, args);
pw.print("\n");
+ pw.flush();
} catch (RemoteException e) {
pw.println("got a RemoteException while dumping the service");
}
@@ -10123,12 +7419,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mServiceConnections.size() > 0) {
if (needSep) pw.println(" ");
pw.println(" Connection bindings to services:");
- Iterator<ConnectionRecord> it
+ Iterator<ArrayList<ConnectionRecord>> it
= mServiceConnections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord r = it.next();
- pw.print(" * "); pw.println(r);
- r.dump(pw, " ");
+ ArrayList<ConnectionRecord> r = it.next();
+ for (int i=0; i<r.size(); i++) {
+ pw.print(" * "); pw.println(r.get(i));
+ r.get(i).dump(pw, " ");
+ }
}
needSep = true;
}
@@ -10145,10 +7443,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mProvidersByClass.size() > 0) {
if (needSep) pw.println(" ");
pw.println(" Published content providers (by class):");
- Iterator it = mProvidersByClass.entrySet().iterator();
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByClass.entrySet().iterator();
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
pw.print(" * "); pw.println(r);
r.dump(pw, " ");
}
@@ -10158,10 +7457,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mProvidersByName.size() > 0) {
pw.println(" ");
pw.println(" Authority to provider mappings:");
- Iterator it = mProvidersByName.entrySet().iterator();
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByName.entrySet().iterator();
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
pw.print(" "); pw.print(e.getKey()); pw.print(": ");
pw.println(r);
}
@@ -10228,7 +7528,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
String prefix, String label, boolean complete) {
TaskRecord lastTask = null;
for (int i=list.size()-1; i>=0; i--) {
- HistoryRecord r = (HistoryRecord)list.get(i);
+ ActivityRecord r = (ActivityRecord)list.get(i);
final boolean full = complete || !r.inHistory;
if (lastTask != r.task) {
lastTask = r.task;
@@ -10261,7 +7561,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
String prefix, String normalLabel, String persistentLabel,
boolean inclOomAdj) {
int numPers = 0;
- for (int i=list.size()-1; i>=0; i--) {
+ final int N = list.size()-1;
+ for (int i=N; i>=0; i--) {
ProcessRecord r = (ProcessRecord)list.get(i);
if (false) {
pw.println(prefix + (r.persistent ? persistentLabel : normalLabel)
@@ -10279,6 +7580,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
oomAdj = buildOomTag("svc", " ", r.setAdj, SECONDARY_SERVER_ADJ);
} else if (r.setAdj >= BACKUP_APP_ADJ) {
oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ);
+ } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) {
+ oomAdj = buildOomTag("hvy ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ);
+ } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) {
+ oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ);
} else if (r.setAdj >= VISIBLE_APP_ADJ) {
oomAdj = buildOomTag("vis ", null, r.setAdj, VISIBLE_APP_ADJ);
} else if (r.setAdj >= FOREGROUND_APP_ADJ) {
@@ -10304,10 +7609,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
prefix, (r.persistent ? persistentLabel : normalLabel),
- i, oomAdj, schedGroup, r.toShortString(), r.adjType));
+ N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
if (r.adjSource != null || r.adjTarget != null) {
- pw.println(prefix + " " + r.adjTarget
- + "<=" + r.adjSource);
+ pw.print(prefix);
+ pw.print(" ");
+ if (r.adjTarget instanceof ComponentName) {
+ pw.print(((ComponentName)r.adjTarget).flattenToShortString());
+ } else if (r.adjTarget != null) {
+ pw.print(r.adjTarget.toString());
+ } else {
+ pw.print("{null}");
+ }
+ pw.print("<=");
+ if (r.adjSource instanceof ProcessRecord) {
+ pw.print("Proc{");
+ pw.print(((ProcessRecord)r.adjSource).toShortString());
+ pw.println("}");
+ } else if (r.adjSource != null) {
+ pw.println(r.adjSource.toString());
+ } else {
+ pw.println("{null}");
+ }
}
} else {
pw.println(String.format("%s%s #%2d: %s",
@@ -10371,22 +7693,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return false;
}
- private final int indexOfTokenLocked(IBinder token) {
- int count = mHistory.size();
-
- // convert the token to an entry in the history.
- int index = -1;
- for (int i=count-1; i>=0; i--) {
- Object o = mHistory.get(i);
- if (o == token) {
- index = i;
- break;
- }
- }
-
- return index;
- }
-
private final void killServicesLocked(ProcessRecord app,
boolean allowRestart) {
// Report disconnected services.
@@ -10394,22 +7700,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// XXX we are letting the client link to the service for
// death notifications.
if (app.services.size() > 0) {
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord r = (ServiceRecord)it.next();
+ ServiceRecord r = it.next();
if (r.connections.size() > 0) {
- Iterator<ConnectionRecord> jt
+ Iterator<ArrayList<ConnectionRecord>> jt
= r.connections.values().iterator();
while (jt.hasNext()) {
- ConnectionRecord c = jt.next();
- if (c.binding.client != app) {
- try {
- //c.conn.connected(r.className, null);
- } catch (Exception e) {
- // todo: this should be asynchronous!
- Slog.w(TAG, "Exception thrown disconnected servce "
- + r.shortName
- + " from app " + app.processName, e);
+ ArrayList<ConnectionRecord> cl = jt.next();
+ for (int i=0; i<cl.size(); i++) {
+ ConnectionRecord c = cl.get(i);
+ if (c.binding.client != app) {
+ try {
+ //c.conn.connected(r.className, null);
+ } catch (Exception e) {
+ // todo: this should be asynchronous!
+ Slog.w(TAG, "Exception thrown disconnected servce "
+ + r.shortName
+ + " from app " + app.processName, e);
+ }
}
}
}
@@ -10431,15 +7740,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (app.services.size() != 0) {
// Any services running in the application need to be placed
// back in the pending list.
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord sr = (ServiceRecord)it.next();
+ ServiceRecord sr = it.next();
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
sr.app = null;
sr.executeNesting = 0;
- mStoppingServices.remove(sr);
+ if (mStoppingServices.remove(sr)) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+ }
boolean hasClients = sr.bindings.size() > 0;
if (hasClients) {
@@ -10492,6 +7803,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ServiceRecord sr = mStoppingServices.get(i);
if (sr.app == app) {
mStoppingServices.remove(i);
+ if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
}
}
@@ -10572,9 +7884,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Remove published content providers.
if (!app.pubProviders.isEmpty()) {
- Iterator it = app.pubProviders.values().iterator();
+ Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
while (it.hasNext()) {
- ContentProviderRecord cpr = (ContentProviderRecord)it.next();
+ ContentProviderRecord cpr = it.next();
cpr.provider = null;
cpr.app = null;
@@ -10666,6 +7978,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_PROCESSES) Slog.v(TAG,
"Removing non-persistent process during cleanup: " + app);
mProcessNames.remove(app.processName, app.info.uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
@@ -10707,8 +8023,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
int NL = mLaunchingProviders.size();
boolean restart = false;
for (int i=0; i<NL; i++) {
- ContentProviderRecord cpr = (ContentProviderRecord)
- mLaunchingProviders.get(i);
+ ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.launchingApp == app) {
if (!alwaysBad && !app.bad) {
restart = true;
@@ -10752,11 +8067,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.app != null && r.app.persistent) {
info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
}
- for (ConnectionRecord conn : r.connections.values()) {
- if (conn.clientLabel != 0) {
- info.clientPackage = conn.binding.client.info.packageName;
- info.clientLabel = conn.clientLabel;
- break;
+
+ for (ArrayList<ConnectionRecord> connl : r.connections.values()) {
+ for (int i=0; i<connl.size(); i++) {
+ ConnectionRecord conn = connl.get(i);
+ if (conn.clientLabel != 0) {
+ info.clientPackage = conn.binding.client.info.packageName;
+ info.clientLabel = conn.clientLabel;
+ return info;
+ }
}
}
return info;
@@ -10791,9 +8110,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
ServiceRecord r = mServices.get(name);
if (r != null) {
- for (ConnectionRecord conn : r.connections.values()) {
- if (conn.clientIntent != null) {
- return conn.clientIntent;
+ for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
+ for (int i=0; i<conn.size(); i++) {
+ if (conn.get(i).clientIntent != null) {
+ return conn.get(i).clientIntent;
+ }
}
}
}
@@ -10831,7 +8152,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r == null) {
try {
ResolveInfo rInfo =
- ActivityThread.getPackageManager().resolveService(
+ AppGlobals.getPackageManager().resolveService(
service, resolvedType, 0);
ServiceInfo sInfo =
rInfo != null ? rInfo.serviceInfo : null;
@@ -10888,7 +8209,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r == null) {
try {
ResolveInfo rInfo =
- ActivityThread.getPackageManager().resolveService(
+ AppGlobals.getPackageManager().resolveService(
service, resolvedType, STOCK_PM_FLAGS);
ServiceInfo sInfo =
rInfo != null ? rInfo.serviceInfo : null;
@@ -10967,18 +8288,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return;
}
- int i = 0;
- while (i < N) {
+ while (r.pendingStarts.size() > 0) {
try {
- ServiceRecord.StartItem si = r.pendingStarts.get(i);
- if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: "
- + r.name + " " + r.intent + " args=" + si.intent);
- if (si.intent == null && N > 1) {
+ ServiceRecord.StartItem si = r.pendingStarts.remove(0);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: "
+ + r + " " + r.intent + " args=" + si.intent);
+ if (si.intent == null) {
// If somehow we got a dummy start at the front, then
// just drop it here.
- i++;
continue;
}
+ si.deliveredTime = SystemClock.uptimeMillis();
+ r.deliveredStarts.add(si);
+ si.deliveryCount++;
+ if (si.targetPermissionUid >= 0) {
+ grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
+ r.packageName, si.intent, si.getUriPermissionsLocked());
+ }
+ if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING start of " + r);
bumpServiceExecutingLocked(r);
if (!oomAdjusted) {
oomAdjusted = true;
@@ -10992,27 +8319,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
flags |= Service.START_FLAG_REDELIVERY;
}
r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
- si.deliveredTime = SystemClock.uptimeMillis();
- r.deliveredStarts.add(si);
- si.deliveryCount++;
- i++;
} catch (RemoteException e) {
// Remote process gone... we'll let the normal cleanup take
// care of this.
+ if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r);
break;
} catch (Exception e) {
Slog.w(TAG, "Unexpected exception", e);
break;
}
}
- if (i == N) {
- r.pendingStarts.clear();
- } else {
- while (i > 0) {
- i--;
- r.pendingStarts.remove(i);
- }
- }
}
private final boolean requestServiceBindingLocked(ServiceRecord r,
@@ -11023,9 +8339,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
+ if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bind of " + r
+ + " in " + i + ": shouldUnbind=" + i.hasBound);
bumpServiceExecutingLocked(r);
- if (DEBUG_SERVICE) Slog.v(TAG, "Connecting binding " + i
- + ": shouldUnbind=" + i.hasBound);
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
if (!rebind) {
i.requested = true;
@@ -11033,6 +8349,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
i.hasBound = true;
i.doRebind = false;
} catch (RemoteException e) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
return false;
}
}
@@ -11059,13 +8376,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
app.services.add(r);
+ if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING create of " + r + " " + r.intent);
bumpServiceExecutingLocked(r);
updateLruProcessLocked(app, true, true);
boolean created = false;
try {
- if (DEBUG_SERVICE) Slog.v(TAG, "Scheduling start service: "
- + r.name + " " + r.intent);
mStringBuilder.setLength(0);
r.intent.getIntent().toShortString(mStringBuilder, false, true);
EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
@@ -11095,7 +8411,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.lastStartId < 1) {
r.lastStartId = 1;
}
- r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, null));
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
}
sendServiceArgsLocked(r, true);
@@ -11115,6 +8431,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (N > 0) {
for (int i=N-1; i>=0; i--) {
ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+ si.removeUriPermissionsLocked();
if (si.intent == null) {
// We'll generate this again if needed.
} else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
@@ -11224,8 +8541,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return true;
}
- if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up service " + r.name
- + " " + r.intent);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
// We are now bringing the service up, so no longer in the
// restarting state.
@@ -11276,27 +8592,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (!force) {
// XXX should probably keep a count of the number of auto-create
// connections directly in the service.
- Iterator<ConnectionRecord> it = r.connections.values().iterator();
+ Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord cr = it.next();
- if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
- return;
+ ArrayList<ConnectionRecord> cr = it.next();
+ for (int i=0; i<cr.size(); i++) {
+ if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+ return;
+ }
}
}
}
// Report to all of the connections that the service is no longer
// available.
- Iterator<ConnectionRecord> it = r.connections.values().iterator();
+ Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord c = it.next();
- try {
- // todo: shouldn't be a synchronous call!
- c.conn.connected(r.name, null);
- } catch (Exception e) {
- Slog.w(TAG, "Failure disconnecting service " + r.name +
- " to connection " + c.conn.asBinder() +
- " (in " + c.binding.client.processName + ")", e);
+ ArrayList<ConnectionRecord> c = it.next();
+ for (int i=0; i<c.size(); i++) {
+ try {
+ c.get(i).conn.connected(r.name, null);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure disconnecting service " + r.name +
+ " to connection " + c.get(i).conn.asBinder() +
+ " (in " + c.get(i).binding.client.processName + ")", e);
+ }
}
}
}
@@ -11310,6 +8629,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ ": hasBound=" + ibr.hasBound);
if (r.app != null && r.app.thread != null && ibr.hasBound) {
try {
+ if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bring down unbind of " + r
+ + " for " + ibr);
bumpServiceExecutingLocked(r);
updateOomAdjLocked(r.app);
ibr.hasBound = false;
@@ -11324,15 +8645,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down service " + r.name
- + " " + r.intent);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
System.identityHashCode(r), r.shortName,
(r.app != null) ? r.app.pid : -1);
mServices.remove(r.name);
mServicesByIntent.remove(r.intent);
- if (localLOGV) Slog.v(TAG, "BRING DOWN SERVICE: " + r.shortName);
r.totalRestartCount = 0;
unscheduleServiceRestartLocked(r);
@@ -11341,8 +8660,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
for (int i=0; i<N; i++) {
if (mPendingServices.get(i) == r) {
mPendingServices.remove(i);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Removed pending service: " + r.shortName);
+ if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r);
i--;
N--;
}
@@ -11354,7 +8672,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.foregroundNoti = null;
// Clear start entries.
- r.deliveredStarts.clear();
+ r.clearDeliveredStartsLocked();
r.pendingStarts.clear();
if (r.app != null) {
@@ -11364,8 +8682,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.app.services.remove(r);
if (r.app.thread != null) {
try {
- if (DEBUG_SERVICE) Slog.v(TAG,
- "Stopping service: " + r.shortName);
+ if (DEBUG_SERVICE) {
+ RuntimeException here = new RuntimeException();
+ here.fillInStackTrace();
+ Slog.v(TAG, ">>> EXECUTING stop of " + r, here);
+ }
bumpServiceExecutingLocked(r);
mStoppingServices.add(r);
updateOomAdjLocked(r.app);
@@ -11378,11 +8699,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
updateServiceForegroundLocked(r.app, false);
} else {
if (DEBUG_SERVICE) Slog.v(
- TAG, "Removed service that has no process: " + r.shortName);
+ TAG, "Removed service that has no process: " + r);
}
} else {
if (DEBUG_SERVICE) Slog.v(
- TAG, "Removed service that is not running: " + r.shortName);
+ TAG, "Removed service that is not running: " + r);
}
}
@@ -11414,9 +8735,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
? res.permission : "private to package");
}
ServiceRecord r = res.record;
+ int targetPermissionUid = checkGrantUriPermissionFromIntentLocked(
+ callingUid, r.packageName, service);
if (unscheduleServiceRestartLocked(r)) {
- if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: "
- + r.shortName);
+ if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
}
r.startRequested = true;
r.callStart = false;
@@ -11424,7 +8746,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.lastStartId < 1) {
r.lastStartId = 1;
}
- r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, service));
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+ service, targetPermissionUid));
r.lastActivity = SystemClock.uptimeMillis();
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
@@ -11549,7 +8872,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
if (si != null) {
while (r.deliveredStarts.size() > 0) {
- if (r.deliveredStarts.remove(0) == si) {
+ ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
+ cur.removeUriPermissionsLocked();
+ if (cur == si) {
break;
}
}
@@ -11625,7 +8950,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
public void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
boolean anyForeground = false;
- for (ServiceRecord sr : (HashSet<ServiceRecord>)proc.services) {
+ for (ServiceRecord sr : proc.services) {
if (sr.isForeground) {
anyForeground = true;
break;
@@ -11659,14 +8984,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ ") when binding service " + service);
}
- HistoryRecord activity = null;
+ ActivityRecord activity = null;
if (token != null) {
- int aindex = indexOfTokenLocked(token);
+ int aindex = mMainStack.indexOfTokenLocked(token);
if (aindex < 0) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
- activity = (HistoryRecord)mHistory.get(aindex);
+ activity = (ActivityRecord)mMainStack.mHistory.get(aindex);
}
int clientLabel = 0;
@@ -11707,7 +9032,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (unscheduleServiceRestartLocked(s)) {
if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
- + s.shortName);
+ + s);
}
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
@@ -11715,7 +9040,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
- s.connections.put(binder, c);
+ ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+ if (clist == null) {
+ clist = new ArrayList<ConnectionRecord>();
+ s.connections.put(binder, clist);
+ }
+ clist.add(c);
b.connections.add(c);
if (activity != null) {
if (activity.connections == null) {
@@ -11724,7 +9054,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
activity.connections.add(c);
}
b.client.connections.add(c);
- mServiceConnections.put(binder, c);
+ clist = mServiceConnections.get(binder);
+ if (clist == null) {
+ clist = new ArrayList<ConnectionRecord>();
+ mServiceConnections.put(binder, clist);
+ }
+ clist.add(c);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
@@ -11770,12 +9105,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return 1;
}
- private void removeConnectionLocked(
- ConnectionRecord c, ProcessRecord skipApp, HistoryRecord skipAct) {
+ void removeConnectionLocked(
+ ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
- s.connections.remove(binder);
+ ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+ if (clist != null) {
+ clist.remove(c);
+ if (clist.size() == 0) {
+ s.connections.remove(binder);
+ }
+ }
b.connections.remove(c);
if (c.activity != null && c.activity != skipAct) {
if (c.activity.connections != null) {
@@ -11785,7 +9126,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (b.client != skipApp) {
b.client.connections.remove(c);
}
- mServiceConnections.remove(binder);
+ clist = mServiceConnections.get(binder);
+ if (clist != null) {
+ clist.remove(c);
+ if (clist.size() == 0) {
+ mServiceConnections.remove(binder);
+ }
+ }
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
@@ -11796,6 +9143,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
&& b.intent.hasBound) {
try {
+ if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING unbind of " + s
+ + " from " + b);
bumpServiceExecutingLocked(s);
updateOomAdjLocked(s.app);
b.intent.hasBound = false;
@@ -11818,8 +9167,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
IBinder binder = connection.asBinder();
if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder);
- ConnectionRecord r = mServiceConnections.get(binder);
- if (r == null) {
+ ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+ if (clist == null) {
Slog.w(TAG, "Unbind failed: could not find connection for "
+ connection.asBinder());
return false;
@@ -11827,11 +9176,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
- removeConnectionLocked(r, null, null);
+ while (clist.size() > 0) {
+ ConnectionRecord r = clist.get(0);
+ removeConnectionLocked(r, null, null);
- if (r.binding.service.app != null) {
- // This could have made the service less important.
- updateOomAdjLocked(r.binding.service.app);
+ if (r.binding.service.app != null) {
+ // This could have made the service less important.
+ updateOomAdjLocked(r.binding.service.app);
+ }
}
Binder.restoreCallingIdentity(origId);
@@ -11854,7 +9206,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
- if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING SERVICE " + r.name
+ if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
+ " " + intent + ": " + service);
if (r != null) {
Intent.FilterComparison filter
@@ -11865,26 +9217,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
b.requested = true;
b.received = true;
if (r.connections.size() > 0) {
- Iterator<ConnectionRecord> it
+ Iterator<ArrayList<ConnectionRecord>> it
= r.connections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord c = it.next();
- if (!filter.equals(c.binding.intent.intent)) {
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Not publishing to: " + c);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Bound intent: " + c.binding.intent.intent);
- if (DEBUG_SERVICE) Slog.v(
- TAG, "Published intent: " + intent);
- continue;
- }
- if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
- try {
- c.conn.connected(r.name, service);
- } catch (Exception e) {
- Slog.w(TAG, "Failure sending service " + r.name +
- " to connection " + c.conn.asBinder() +
- " (in " + c.binding.client.processName + ")", e);
+ ArrayList<ConnectionRecord> clist = it.next();
+ for (int i=0; i<clist.size(); i++) {
+ ConnectionRecord c = clist.get(i);
+ if (!filter.equals(c.binding.intent.intent)) {
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Not publishing to: " + c);
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Bound intent: " + c.binding.intent.intent);
+ if (DEBUG_SERVICE) Slog.v(
+ TAG, "Published intent: " + intent);
+ continue;
+ }
+ if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
+ try {
+ c.conn.connected(r.name, service);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failure sending service " + r.name +
+ " to connection " + c.conn.asBinder() +
+ " (in " + c.binding.client.processName + ")", e);
+ }
}
}
}
@@ -11945,9 +9300,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ServiceRecord r = (ServiceRecord)token;
boolean inStopping = mStoppingServices.contains(token);
if (r != null) {
- if (DEBUG_SERVICE) Slog.v(TAG, "DONE EXECUTING SERVICE " + r.name
- + ": nesting=" + r.executeNesting
- + ", inStopping=" + inStopping);
if (r != token) {
Slog.w(TAG, "Done executing service " + r.name
+ " with incorrect token: given " + token
@@ -12004,13 +9356,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
} else {
- Slog.w(TAG, "Done executing unknown service " + r.name
- + " with token " + token);
+ Slog.w(TAG, "Done executing unknown service from pid "
+ + Binder.getCallingPid());
}
}
}
public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
+ + ": nesting=" + r.executeNesting
+ + ", inStopping=" + inStopping + ", app=" + r.app);
r.executeNesting--;
if (r.executeNesting <= 0 && r.app != null) {
r.app.executingServices.remove(r);
@@ -12018,6 +9373,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
}
if (inStopping) {
+ if (DEBUG_SERVICE) Slog.v(TAG, "doneExecuting remove stopping " + r);
mStoppingServices.remove(r);
}
updateOomAdjLocked(r.app);
@@ -12452,7 +9808,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Always okay.
} else if (callerApp == null || !callerApp.persistent) {
try {
- if (ActivityThread.getPackageManager().isProtectedBroadcast(
+ if (AppGlobals.getPackageManager().isProtectedBroadcast(
intent.getAction())) {
String msg = "Permission Denial: not allowed to send broadcast "
+ intent.getAction() + " from pid="
@@ -12511,7 +9867,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
try {
if (intent.getComponent() != null) {
// Broadcast is going to one specific receiver class...
- ActivityInfo ai = ActivityThread.getPackageManager().
+ ActivityInfo ai = AppGlobals.getPackageManager().
getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS);
if (ai != null) {
receivers = new ArrayList();
@@ -12524,7 +9880,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
receivers =
- ActivityThread.getPackageManager().queryIntentReceivers(
+ AppGlobals.getPackageManager().queryIntentReceivers(
intent, resolvedType, STOCK_PM_FLAGS);
}
registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);
@@ -13276,7 +10632,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.callingUid != Process.SYSTEM_UID &&
r.requiredPermission != null) {
try {
- perm = ActivityThread.getPackageManager().
+ perm = AppGlobals.getPackageManager().
checkPermission(r.requiredPermission,
info.activityInfo.applicationInfo.packageName);
} catch (RemoteException e) {
@@ -13533,7 +10889,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* configuration.
*/
public boolean updateConfigurationLocked(Configuration values,
- HistoryRecord starting) {
+ ActivityRecord starting) {
int changes = 0;
boolean kept = true;
@@ -13602,18 +10958,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// If the configuration changed, and the caller is not already
// in the process of starting an activity, then find the top
// activity to check if its configuration needs to change.
- starting = topRunningActivityLocked(null);
+ starting = mMainStack.topRunningActivityLocked(null);
}
if (starting != null) {
- kept = ensureActivityConfigurationLocked(starting, changes);
+ kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
if (kept) {
// If this didn't result in the starting activity being
// destroyed, then we need to make sure at this point that all
// other activities are made visible.
if (DEBUG_SWITCH) Slog.i(TAG, "Config didn't destroy " + starting
+ ", ensuring others are correct.");
- ensureActivitiesVisibleLocked(starting, changes);
+ mMainStack.ensureActivitiesVisibleLocked(starting, changes);
}
}
@@ -13623,160 +10979,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return kept;
}
-
- private final boolean relaunchActivityLocked(HistoryRecord r,
- int changes, boolean andResume) {
- List<ResultInfo> results = null;
- List<Intent> newIntents = null;
- if (andResume) {
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
- + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
- EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
- : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
-
- r.startFreezingScreenLocked(r.app, 0);
-
- try {
- if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
- r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
- changes, !andResume, mConfiguration);
- // Note: don't need to call pauseIfSleepingLocked() here, because
- // the caller will only pass in 'andResume' if this activity is
- // currently resumed, which implies we aren't sleeping.
- } catch (RemoteException e) {
- return false;
- }
-
- if (andResume) {
- r.results = null;
- r.newIntents = null;
- reportResumedActivityLocked(r);
- }
-
- return true;
- }
-
- /**
- * Make sure the given activity matches the current configuration. Returns
- * false if the activity had to be destroyed. Returns true if the
- * configuration is the same, or the activity will remain running as-is
- * for whatever reason. Ensures the HistoryRecord is updated with the
- * correct configuration and all other bookkeeping is handled.
- */
- private final boolean ensureActivityConfigurationLocked(HistoryRecord r,
- int globalChanges) {
- if (mConfigWillChange) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Skipping config check (will change): " + r);
- return true;
- }
-
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Ensuring correct configuration: " + r);
-
- // Short circuit: if the two configurations are the exact same
- // object (the common case), then there is nothing to do.
- Configuration newConfig = mConfiguration;
- if (r.configuration == newConfig) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration unchanged in " + r);
- return true;
- }
-
- // We don't worry about activities that are finishing.
- if (r.finishing) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration doesn't matter in finishing " + r);
- r.stopFreezingScreenLocked(false);
- return true;
- }
-
- // Okay we now are going to make this activity have the new config.
- // But then we need to figure out how it needs to deal with that.
- Configuration oldConfig = r.configuration;
- r.configuration = newConfig;
-
- // If the activity isn't currently running, just leave the new
- // configuration and it will pick that up next time it starts.
- if (r.app == null || r.app.thread == null) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration doesn't matter not running " + r);
- r.stopFreezingScreenLocked(false);
- return true;
- }
-
- // If the activity isn't persistent, there is a chance we will
- // need to restart it.
- if (!r.persistent) {
-
- // Figure out what has changed between the two configurations.
- int changes = oldConfig.diff(newConfig);
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
- + Integer.toHexString(changes) + ", handles=0x"
- + Integer.toHexString(r.info.configChanges)
- + ", newConfig=" + newConfig);
- }
- if ((changes&(~r.info.configChanges)) != 0) {
- // Aha, the activity isn't handling the change, so DIE DIE DIE.
- r.configChangeFlags |= changes;
- r.startFreezingScreenLocked(r.app, globalChanges);
- if (r.app == null || r.app.thread == null) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is destroying non-running " + r);
- destroyActivityLocked(r, true);
- } else if (r.state == ActivityState.PAUSING) {
- // A little annoying: we are waiting for this activity to
- // finish pausing. Let's not do anything now, but just
- // flag that it needs to be restarted when done pausing.
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is skipping already pausing " + r);
- r.configDestroy = true;
- return true;
- } else if (r.state == ActivityState.RESUMED) {
- // Try to optimize this case: the configuration is changing
- // and we need to restart the top, resumed activity.
- // Instead of doing the normal handshaking, just say
- // "restart!".
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is restarting resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, true);
- r.configChangeFlags = 0;
- } else {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is restarting non-resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, false);
- r.configChangeFlags = 0;
- }
-
- // All done... tell the caller we weren't able to keep this
- // activity around.
- return false;
- }
- }
-
- // Default case: the activity can handle this new configuration, so
- // hand it over. Note that we don't need to give it the new
- // configuration, since we always send configuration changes to all
- // process when they happen so it can just use whatever configuration
- // it last got.
- if (r.app != null && r.app.thread != null) {
- try {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
- r.app.thread.scheduleActivityConfigurationChanged(r);
- } catch (RemoteException e) {
- // If process died, whatever.
- }
- }
- r.stopFreezingScreenLocked(false);
-
- return true;
- }
/**
* Save the locale. You must be inside a synchronized (this) block.
@@ -13868,15 +11070,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
app.adjType = "exec-service";
} else if (app.foregroundServices) {
// The user is aware of this app, so make it visible.
- adj = VISIBLE_APP_ADJ;
+ adj = PERCEPTIBLE_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "foreground-service";
} else if (app.forcingToForeground != null) {
// The user is aware of this app, so make it visible.
- adj = VISIBLE_APP_ADJ;
+ adj = PERCEPTIBLE_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "force-foreground";
app.adjSource = app.forcingToForeground;
+ } else if (app == mHeavyWeightProcess) {
+ // We don't want to kill the current heavy-weight process.
+ adj = HEAVY_WEIGHT_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.adjType = "heavy";
} else if (app == mHomeProcess) {
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
@@ -13891,7 +11098,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
app.adjType = "bg-activities";
N = app.activities.size();
for (int j=0; j<N; j++) {
- if (((HistoryRecord)app.activities.get(j)).visible) {
+ if (app.activities.get(j).visible) {
// This app has a visible activity!
app.hidden = false;
adj = VISIBLE_APP_ADJ;
@@ -13934,9 +11141,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long now = SystemClock.uptimeMillis();
// This process is more important if the top activity is
// bound to the service.
- Iterator jt = app.services.iterator();
+ Iterator<ServiceRecord> jt = app.services.iterator();
while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) {
- ServiceRecord s = (ServiceRecord)jt.next();
+ ServiceRecord s = jt.next();
if (s.startRequested) {
if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
@@ -13957,62 +11164,65 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- Iterator<ConnectionRecord> kt
+ Iterator<ArrayList<ConnectionRecord>> kt
= s.connections.values().iterator();
while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
- // XXX should compute this based on the max of
- // all connected clients.
- ConnectionRecord cr = kt.next();
- if (cr.binding.client == app) {
- // Binding to ourself is not interesting.
- continue;
- }
- if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
- ProcessRecord client = cr.binding.client;
- int myHiddenAdj = hiddenAdj;
- if (myHiddenAdj > client.hiddenAdj) {
- if (client.hiddenAdj > VISIBLE_APP_ADJ) {
- myHiddenAdj = client.hiddenAdj;
- } else {
- myHiddenAdj = VISIBLE_APP_ADJ;
- }
+ ArrayList<ConnectionRecord> clist = kt.next();
+ for (int i=0; i<clist.size() && adj > FOREGROUND_APP_ADJ; i++) {
+ // XXX should compute this based on the max of
+ // all connected clients.
+ ConnectionRecord cr = clist.get(i);
+ if (cr.binding.client == app) {
+ // Binding to ourself is not interesting.
+ continue;
}
- int clientAdj = computeOomAdjLocked(
- client, myHiddenAdj, TOP_APP, true);
- if (adj > clientAdj) {
- adj = clientAdj > VISIBLE_APP_ADJ
- ? clientAdj : VISIBLE_APP_ADJ;
- if (!client.hidden) {
- app.hidden = false;
+ if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+ ProcessRecord client = cr.binding.client;
+ int myHiddenAdj = hiddenAdj;
+ if (myHiddenAdj > client.hiddenAdj) {
+ if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
+ myHiddenAdj = client.hiddenAdj;
+ } else {
+ myHiddenAdj = VISIBLE_APP_ADJ;
+ }
}
+ int clientAdj = computeOomAdjLocked(
+ client, myHiddenAdj, TOP_APP, true);
+ if (adj > clientAdj) {
+ adj = clientAdj >= VISIBLE_APP_ADJ
+ ? clientAdj : VISIBLE_APP_ADJ;
+ if (!client.hidden) {
+ app.hidden = false;
+ }
+ app.adjType = "service";
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE;
+ app.adjSource = cr.binding.client;
+ app.adjTarget = s.name;
+ }
+ if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+ if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ }
+ }
+ }
+ ActivityRecord a = cr.activity;
+ //if (a != null) {
+ // Slog.i(TAG, "Connection to " + a ": state=" + a.state);
+ //}
+ if (a != null && adj > FOREGROUND_APP_ADJ &&
+ (a.state == ActivityState.RESUMED
+ || a.state == ActivityState.PAUSING)) {
+ adj = FOREGROUND_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.hidden = false;
app.adjType = "service";
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE;
- app.adjSource = cr.binding.client;
- app.adjTarget = s.serviceInfo.name;
- }
- if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
- if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- }
+ app.adjSource = a;
+ app.adjTarget = s.name;
}
}
- HistoryRecord a = cr.activity;
- //if (a != null) {
- // Slog.i(TAG, "Connection to " + a ": state=" + a.state);
- //}
- if (a != null && adj > FOREGROUND_APP_ADJ &&
- (a.state == ActivityState.RESUMED
- || a.state == ActivityState.PAUSING)) {
- adj = FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- app.hidden = false;
- app.adjType = "service";
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo
- .REASON_SERVICE_IN_USE;
- app.adjSource = a;
- app.adjTarget = s.serviceInfo.name;
- }
}
}
}
@@ -14031,10 +11241,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (app.pubProviders.size() != 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- Iterator jt = app.pubProviders.values().iterator();
+ Iterator<ContentProviderRecord> jt = app.pubProviders.values().iterator();
while (jt.hasNext() && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- ContentProviderRecord cpr = (ContentProviderRecord)jt.next();
+ ContentProviderRecord cpr = jt.next();
if (cpr.clients.size() != 0) {
Iterator<ProcessRecord> kt = cpr.clients.iterator();
while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
@@ -14063,7 +11273,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_PROVIDER_IN_USE;
app.adjSource = client;
- app.adjTarget = cpr.info.name;
+ app.adjTarget = cpr.name;
}
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -14079,7 +11289,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.hidden = false;
app.adjType = "provider";
- app.adjTarget = cpr.info.name;
+ app.adjTarget = cpr.name;
}
}
}
@@ -14091,7 +11301,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
if (adj > app.maxAdj) {
adj = app.maxAdj;
- if (app.maxAdj <= VISIBLE_APP_ADJ) {
+ if (app.maxAdj <= PERCEPTIBLE_APP_ADJ) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
}
}
@@ -14127,8 +11337,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private final boolean canGcNowLocked() {
return mParallelBroadcasts.size() == 0
&& mOrderedBroadcasts.size() == 0
- && (mSleeping || (mResumedActivity != null &&
- mResumedActivity.idle));
+ && (mSleeping || (mMainStack.mResumedActivity != null &&
+ mMainStack.mResumedActivity.idle));
}
/**
@@ -14143,7 +11353,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (canGcNowLocked()) {
while (mProcessesToGc.size() > 0) {
ProcessRecord proc = mProcessesToGc.remove(0);
- if (proc.curRawAdj > VISIBLE_APP_ADJ || proc.reportLowMemory) {
+ if (proc.curRawAdj > PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
<= SystemClock.uptimeMillis()) {
// To avoid spamming the system, we will GC processes one
@@ -14232,6 +11442,62 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ final void checkExcessiveWakeLocksLocked(boolean doKills) {
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ if (mLastWakeLockCheckTime == 0) {
+ doKills = false;
+ }
+ if (stats.isScreenOn()) {
+ doKills = false;
+ }
+ final long curRealtime = SystemClock.elapsedRealtime();
+ final long timeSince = curRealtime - mLastWakeLockCheckTime;
+ mLastWakeLockCheckTime = curRealtime;
+ if (timeSince < (WAKE_LOCK_CHECK_DELAY/3)) {
+ doKills = false;
+ }
+ int i = mLruProcesses.size();
+ while (i > 0) {
+ i--;
+ ProcessRecord app = mLruProcesses.get(i);
+ if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {
+ long wtime;
+ synchronized (stats) {
+ wtime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, curRealtime);
+ }
+ long timeUsed = wtime - app.lastWakeTime;
+ if (false) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Wake for ");
+ app.toShortString(sb);
+ sb.append(": over ");
+ TimeUtils.formatDuration(timeSince, sb);
+ sb.append(" used ");
+ TimeUtils.formatDuration(timeUsed, sb);
+ sb.append(" (");
+ sb.append((timeUsed*100)/timeSince);
+ sb.append("%)");
+ Slog.i(TAG, sb.toString());
+ }
+ // If a process has held a wake lock for more
+ // than 50% of the time during this period,
+ // that sounds pad. Kill!
+ if (doKills && timeSince > 0
+ && ((timeUsed*100)/timeSince) >= 50) {
+ Slog.i(TAG, "Excessive wake lock in " + app.processName
+ + " (pid " + app.pid + "): held " + timeUsed
+ + " during " + timeSince);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "excessive wake lock");
+ Process.killProcessQuiet(app.pid);
+ } else {
+ app.lastWakeTime = wtime;
+ }
+ }
+ }
+ }
+
private final boolean updateOomAdjLocked(
ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
app.hiddenAdj = hiddenAdj;
@@ -14254,6 +11520,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Likewise do a gc when an app is moving in to the
// background (such as a service stopping).
scheduleAppGcLocked(app);
+ // And note its current wake lock time.
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, SystemClock.elapsedRealtime());
+ }
}
app.setRawAdj = app.curRawAdj;
}
@@ -14298,19 +11570,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return true;
}
- private final HistoryRecord resumedAppLocked() {
- HistoryRecord resumedActivity = mResumedActivity;
+ private final ActivityRecord resumedAppLocked() {
+ ActivityRecord resumedActivity = mMainStack.mResumedActivity;
if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = mPausingActivity;
+ resumedActivity = mMainStack.mPausingActivity;
if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = topRunningActivityLocked(null);
+ resumedActivity = mMainStack.topRunningActivityLocked(null);
}
}
return resumedActivity;
}
private final boolean updateOomAdjLocked(ProcessRecord app) {
- final HistoryRecord TOP_ACT = resumedAppLocked();
+ final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
int curAdj = app.curAdj;
final boolean wasHidden = app.curAdj >= HIDDEN_APP_MIN_ADJ
@@ -14331,9 +11603,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return res;
}
- private final boolean updateOomAdjLocked() {
+ final boolean updateOomAdjLocked() {
boolean didOomAdj = true;
- final HistoryRecord TOP_ACT = resumedAppLocked();
+ final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
if (false) {
@@ -14395,7 +11667,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj;
}
- private final void trimApplications() {
+ final void trimApplications() {
synchronized (this) {
int i;
@@ -14515,7 +11787,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (Config.LOGV) Slog.v(
TAG, "Looking to quit " + app.processName);
for (j=0; j<NUMA && canQuit; j++) {
- HistoryRecord r = (HistoryRecord)app.activities.get(j);
+ ActivityRecord r = app.activities.get(j);
if (Config.LOGV) Slog.v(
TAG, " " + r.intent.getComponent().flattenToShortString()
+ ": frozen=" + r.haveState + ", visible=" + r.visible);
@@ -14525,9 +11797,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (canQuit) {
// Finish all of the activities, and then the app itself.
for (j=0; j<NUMA; j++) {
- HistoryRecord r = (HistoryRecord)app.activities.get(j);
+ ActivityRecord r = app.activities.get(j);
if (!r.finishing) {
- destroyActivityLocked(r, false);
+ r.stack.destroyActivityLocked(r, false);
}
r.resultTo = null;
}
@@ -14564,25 +11836,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Finally, if there are too many activities now running, try to
// finish as many as we can to get back down to the limit.
for ( i=0;
- i<mLRUActivities.size()
- && mLRUActivities.size() > curMaxActivities;
+ i<mMainStack.mLRUActivities.size()
+ && mMainStack.mLRUActivities.size() > curMaxActivities;
i++) {
- final HistoryRecord r
- = (HistoryRecord)mLRUActivities.get(i);
+ final ActivityRecord r
+ = (ActivityRecord)mMainStack.mLRUActivities.get(i);
// We can finish this one if we have its icicle saved and
// it is not persistent.
if ((r.haveState || !r.stateNotNeeded) && !r.visible
&& r.stopped && !r.persistent && !r.finishing) {
- final int origSize = mLRUActivities.size();
- destroyActivityLocked(r, true);
+ final int origSize = mMainStack.mLRUActivities.size();
+ r.stack.destroyActivityLocked(r, true);
// This will remove it from the LRU list, so keep
// our index at the same value. Note that this check to
// see if the size changes is just paranoia -- if
// something unexpected happens, we don't want to end up
// in an infinite loop.
- if (origSize > mLRUActivities.size()) {
+ if (origSize > mMainStack.mLRUActivities.size()) {
i--;
}
}
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index dca7a99..1687db1 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,7 @@
package com.android.server.am;
import com.android.server.AttributeCache;
-import com.android.server.am.ActivityManagerService.ActivityState;
+import com.android.server.am.ActivityStack.ActivityState;
import android.app.Activity;
import android.content.ComponentName;
@@ -29,9 +29,11 @@ import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Message;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.view.IApplicationToken;
import java.io.PrintWriter;
@@ -42,8 +44,9 @@ import java.util.HashSet;
/**
* An entry in the history stack, representing an activity.
*/
-class HistoryRecord extends IApplicationToken.Stub {
+class ActivityRecord extends IApplicationToken.Stub {
final ActivityManagerService service; // owner
+ final ActivityStack stack; // owner
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid who started the activity.
final Intent intent; // the original intent that generated us
@@ -68,19 +71,18 @@ class HistoryRecord extends IApplicationToken.Stub {
long startTime; // when we starting launching this activity
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
Configuration configuration; // configuration activity was last running in
- HistoryRecord resultTo; // who started this entry, so will get our reply
+ ActivityRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
final int requestCode; // code given by requester (resultTo)
ArrayList results; // pending ActivityResult objs we have received
HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
ArrayList newIntents; // any pending new intents for single-top mode
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
- HashSet<UriPermission> readUriPermissions; // special access to reading uris.
- HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+ UriPermissionOwner uriPermissions; // current special URI access perms.
ProcessRecord app; // if non-null, hosting application
Bitmap thumbnail; // icon representation of paused screen
CharSequence description; // textual description of paused screen
- ActivityManagerService.ActivityState state; // current state we are in
+ ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
boolean frontOfTask; // is this the root activity of its task?
boolean launchFailed; // set if a launched failed, to abort on 2nd try
@@ -137,11 +139,15 @@ class HistoryRecord extends IApplicationToken.Stub {
if (pendingResults != null) {
pw.print(prefix); pw.print("pendingResults="); pw.println(pendingResults);
}
- if (readUriPermissions != null) {
- pw.print(prefix); pw.print("readUriPermissions="); pw.println(readUriPermissions);
- }
- if (writeUriPermissions != null) {
- pw.print(prefix); pw.print("writeUriPermissions="); pw.println(writeUriPermissions);
+ if (uriPermissions != null) {
+ if (uriPermissions.readUriPermissions != null) {
+ pw.print(prefix); pw.print("readUriPermissions=");
+ pw.println(uriPermissions.readUriPermissions);
+ }
+ if (uriPermissions.writeUriPermissions != null) {
+ pw.print(prefix); pw.print("writeUriPermissions=");
+ pw.println(uriPermissions.writeUriPermissions);
+ }
}
pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed);
pw.print(" haveState="); pw.print(haveState);
@@ -173,12 +179,13 @@ class HistoryRecord extends IApplicationToken.Stub {
}
}
- HistoryRecord(ActivityManagerService _service, ProcessRecord _caller,
+ ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller,
int _launchedFromUid, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
- HistoryRecord _resultTo, String _resultWho, int _reqCode,
+ ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified) {
service = _service;
+ stack = _stack;
info = aInfo;
launchedFromUid = _launchedFromUid;
intent = _intent;
@@ -189,7 +196,7 @@ class HistoryRecord extends IApplicationToken.Stub {
resultTo = _resultTo;
resultWho = _resultWho;
requestCode = _reqCode;
- state = ActivityManagerService.ActivityState.INITIALIZING;
+ state = ActivityState.INITIALIZING;
frontOfTask = false;
launchFailed = false;
haveState = false;
@@ -292,7 +299,14 @@ class HistoryRecord extends IApplicationToken.Stub {
}
}
- void addResultLocked(HistoryRecord from, String resultWho,
+ UriPermissionOwner getUriPermissionsLocked() {
+ if (uriPermissions == null) {
+ uriPermissions = new UriPermissionOwner(service, this);
+ }
+ return uriPermissions;
+ }
+
+ void addResultLocked(ActivityRecord from, String resultWho,
int requestCode, int resultCode,
Intent resultData) {
ActivityResult r = new ActivityResult(from, resultWho,
@@ -303,7 +317,7 @@ class HistoryRecord extends IApplicationToken.Stub {
results.add(r);
}
- void removeResultsLocked(HistoryRecord from, String resultWho,
+ void removeResultsLocked(ActivityRecord from, String resultWho,
int requestCode) {
if (results != null) {
for (int i=results.size()-1; i>=0; i--) {
@@ -327,6 +341,42 @@ class HistoryRecord extends IApplicationToken.Stub {
}
newIntents.add(intent);
}
+
+ /**
+ * Deliver a new Intent to an existing activity, so that its onNewIntent()
+ * method will be called at the proper time.
+ */
+ final void deliverNewIntentLocked(int callingUid, Intent intent) {
+ boolean sent = false;
+ if (state == ActivityState.RESUMED
+ && app != null && app.thread != null) {
+ try {
+ ArrayList<Intent> ar = new ArrayList<Intent>();
+ intent = new Intent(intent);
+ ar.add(intent);
+ service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+ intent, getUriPermissionsLocked());
+ app.thread.scheduleNewIntent(ar, this);
+ sent = true;
+ } catch (RemoteException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Exception thrown sending new intent to " + this, e);
+ } catch (NullPointerException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Exception thrown sending new intent to " + this, e);
+ }
+ }
+ if (!sent) {
+ addNewIntentLocked(new Intent(intent));
+ }
+ }
+
+ void removeUriPermissionsLocked() {
+ if (uriPermissions != null) {
+ uriPermissions.removeUriPermissionsLocked();
+ uriPermissions = null;
+ }
+ }
void pauseKeyDispatchingLocked() {
if (!keysPaused) {
@@ -370,8 +420,8 @@ class HistoryRecord extends IApplicationToken.Stub {
if (startTime != 0) {
final long curTime = SystemClock.uptimeMillis();
final long thisTime = curTime - startTime;
- final long totalTime = service.mInitialStartTime != 0
- ? (curTime - service.mInitialStartTime) : thisTime;
+ final long totalTime = stack.mInitialStartTime != 0
+ ? (curTime - stack.mInitialStartTime) : thisTime;
if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
EventLog.writeEvent(EventLogTags.ACTIVITY_LAUNCH_TIME,
System.identityHashCode(this), shortComponentName,
@@ -387,14 +437,14 @@ class HistoryRecord extends IApplicationToken.Stub {
sb.append(" ms)");
Log.i(ActivityManagerService.TAG, sb.toString());
}
- service.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
+ stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
if (totalTime > 0) {
service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
}
startTime = 0;
- service.mInitialStartTime = 0;
+ stack.mInitialStartTime = 0;
}
- service.reportActivityVisibleLocked(this);
+ stack.reportActivityVisibleLocked(this);
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG, "windowsVisible(): " + this);
if (!nowVisible) {
@@ -403,27 +453,27 @@ class HistoryRecord extends IApplicationToken.Stub {
// Instead of doing the full stop routine here, let's just
// hide any activities we now can, and let them stop when
// the normal idle happens.
- service.processStoppingActivitiesLocked(false);
+ stack.processStoppingActivitiesLocked(false);
} else {
// If this activity was already idle, then we now need to
// make sure we perform the full stop of any activities
// that are waiting to do so. This is because we won't
// do that while they are still waiting for this one to
// become visible.
- final int N = service.mWaitingVisibleActivities.size();
+ final int N = stack.mWaitingVisibleActivities.size();
if (N > 0) {
for (int i=0; i<N; i++) {
- HistoryRecord r = (HistoryRecord)
- service.mWaitingVisibleActivities.get(i);
+ ActivityRecord r = (ActivityRecord)
+ stack.mWaitingVisibleActivities.get(i);
r.waitingVisible = false;
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG,
"Was waiting for visible: " + r);
}
- service.mWaitingVisibleActivities.clear();
+ stack.mWaitingVisibleActivities.clear();
Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- service.mHandler.sendMessage(msg);
+ msg.what = ActivityStack.IDLE_NOW_MSG;
+ stack.mHandler.sendMessage(msg);
}
}
service.scheduleAppGcsLocked();
@@ -437,16 +487,16 @@ class HistoryRecord extends IApplicationToken.Stub {
nowVisible = false;
}
- private HistoryRecord getWaitingHistoryRecordLocked() {
+ private ActivityRecord getWaitingHistoryRecordLocked() {
// First find the real culprit... if we are waiting
// for another app to start, then we have paused dispatching
// for this activity.
- HistoryRecord r = this;
+ ActivityRecord r = this;
if (r.waitingVisible) {
// Hmmm, who might we be waiting for?
- r = service.mResumedActivity;
+ r = stack.mResumedActivity;
if (r == null) {
- r = service.mPausingActivity;
+ r = stack.mPausingActivity;
}
// Both of those null? Fall back to 'this' again
if (r == null) {
@@ -458,7 +508,7 @@ class HistoryRecord extends IApplicationToken.Stub {
}
public boolean keyDispatchingTimedOut() {
- HistoryRecord r;
+ ActivityRecord r;
ProcessRecord anrApp = null;
synchronized(service) {
r = getWaitingHistoryRecordLocked();
@@ -496,7 +546,7 @@ class HistoryRecord extends IApplicationToken.Stub {
/** Returns the key dispatching timeout for this application token. */
public long getKeyDispatchingTimeout() {
synchronized(service) {
- HistoryRecord r = getWaitingHistoryRecordLocked();
+ ActivityRecord r = getWaitingHistoryRecordLocked();
if (r == null || r.app == null
|| r.app.instrumentationClass == null) {
return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
@@ -515,7 +565,6 @@ class HistoryRecord extends IApplicationToken.Stub {
state == ActivityState.RESUMED;
}
-
public String toString() {
if (stringName != null) {
return stringName;
diff --git a/services/java/com/android/server/am/ActivityResult.java b/services/java/com/android/server/am/ActivityResult.java
index 3cc2725..12eba34 100644
--- a/services/java/com/android/server/am/ActivityResult.java
+++ b/services/java/com/android/server/am/ActivityResult.java
@@ -24,9 +24,9 @@ import android.os.Bundle;
* Pending result information to send back to an activity.
*/
class ActivityResult extends ResultInfo {
- final HistoryRecord mFrom;
+ final ActivityRecord mFrom;
- public ActivityResult(HistoryRecord from, String resultWho,
+ public ActivityResult(ActivityRecord from, String resultWho,
int requestCode, int resultCode, Intent data) {
super(resultWho, requestCode, resultCode, data);
mFrom = from;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
new file mode 100644
index 0000000..c21a221
--- /dev/null
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -0,0 +1,3522 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
+
+import android.app.Activity;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import static android.app.IActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.IActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+import static android.app.IActivityManager.START_INTENT_NOT_RESOLVED;
+import static android.app.IActivityManager.START_PERMISSION_DENIED;
+import static android.app.IActivityManager.START_RETURN_INTENT_TO_CALLER;
+import static android.app.IActivityManager.START_SUCCESS;
+import static android.app.IActivityManager.START_SWITCHES_CANCELED;
+import static android.app.IActivityManager.START_TASK_TO_FRONT;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
+import android.app.ResultInfo;
+import android.app.IActivityManager.WaitResult;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.view.WindowManagerPolicy;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * State and management of a single stack of activities.
+ */
+public class ActivityStack {
+ static final String TAG = ActivityManagerService.TAG;
+ static final boolean localLOGV = ActivityManagerService.localLOGV;
+ static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
+ static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE;
+ static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY;
+ static final boolean DEBUG_USER_LEAVING = ActivityManagerService.DEBUG_USER_LEAVING;
+ static final boolean DEBUG_TRANSITION = ActivityManagerService.DEBUG_TRANSITION;
+ static final boolean DEBUG_RESULTS = ActivityManagerService.DEBUG_RESULTS;
+ static final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
+ static final boolean DEBUG_TASKS = ActivityManagerService.DEBUG_TASKS;
+
+ static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS;
+
+ // How long we wait until giving up on the last activity telling us it
+ // is idle.
+ static final int IDLE_TIMEOUT = 10*1000;
+
+ // How long we wait until giving up on the last activity to pause. This
+ // is short because it directly impacts the responsiveness of starting the
+ // next activity.
+ static final int PAUSE_TIMEOUT = 500;
+
+ // How long we can hold the launch wake lock before giving up.
+ static final int LAUNCH_TIMEOUT = 10*1000;
+
+ // How long we wait until giving up on an activity telling us it has
+ // finished destroying itself.
+ static final int DESTROY_TIMEOUT = 10*1000;
+
+ // How long until we reset a task when the user returns to it. Currently
+ // 30 minutes.
+ static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+
+ // Set to false to disable the preview that is shown while a new activity
+ // is being started.
+ static final boolean SHOW_APP_STARTING_PREVIEW = true;
+
+ enum ActivityState {
+ INITIALIZING,
+ RESUMED,
+ PAUSING,
+ PAUSED,
+ STOPPING,
+ STOPPED,
+ FINISHING,
+ DESTROYING,
+ DESTROYED
+ }
+
+ final ActivityManagerService mService;
+ final boolean mMainStack;
+
+ final Context mContext;
+
+ /**
+ * The back history of all previous (and possibly still
+ * running) activities. It contains HistoryRecord objects.
+ */
+ final ArrayList mHistory = new ArrayList();
+
+ /**
+ * List of running activities, sorted by recent usage.
+ * The first entry in the list is the least recently used.
+ * It contains HistoryRecord objects.
+ */
+ final ArrayList mLRUActivities = new ArrayList();
+
+ /**
+ * List of activities that are waiting for a new activity
+ * to become visible before completing whatever operation they are
+ * supposed to do.
+ */
+ final ArrayList<ActivityRecord> mWaitingVisibleActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * List of activities that are ready to be stopped, but waiting
+ * for the next activity to settle down before doing so. It contains
+ * HistoryRecord objects.
+ */
+ final ArrayList<ActivityRecord> mStoppingActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * Animations that for the current transition have requested not to
+ * be considered for the transition animation.
+ */
+ final ArrayList<ActivityRecord> mNoAnimActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * List of activities that are ready to be finished, but waiting
+ * for the previous activity to settle down before doing so. It contains
+ * HistoryRecord objects.
+ */
+ final ArrayList<ActivityRecord> mFinishingActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * List of people waiting to find out about the next launched activity.
+ */
+ final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched
+ = new ArrayList<IActivityManager.WaitResult>();
+
+ /**
+ * List of people waiting to find out about the next visible activity.
+ */
+ final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
+ = new ArrayList<IActivityManager.WaitResult>();
+
+ /**
+ * Set when the system is going to sleep, until we have
+ * successfully paused the current activity and released our wake lock.
+ * At that point the system is allowed to actually sleep.
+ */
+ final PowerManager.WakeLock mGoingToSleep;
+
+ /**
+ * We don't want to allow the device to go to sleep while in the process
+ * of launching an activity. This is primarily to allow alarm intent
+ * receivers to launch an activity and get that to run before the device
+ * goes back to sleep.
+ */
+ final PowerManager.WakeLock mLaunchingActivity;
+
+ /**
+ * When we are in the process of pausing an activity, before starting the
+ * next one, this variable holds the activity that is currently being paused.
+ */
+ ActivityRecord mPausingActivity = null;
+
+ /**
+ * This is the last activity that we put into the paused state. This is
+ * used to determine if we need to do an activity transition while sleeping,
+ * when we normally hold the top activity paused.
+ */
+ ActivityRecord mLastPausedActivity = null;
+
+ /**
+ * Current activity that is resumed, or null if there is none.
+ */
+ ActivityRecord mResumedActivity = null;
+
+ /**
+ * Set when we know we are going to be calling updateConfiguration()
+ * soon, so want to skip intermediate config checks.
+ */
+ boolean mConfigWillChange;
+
+ /**
+ * Set to indicate whether to issue an onUserLeaving callback when a
+ * newly launched activity is being brought in front of us.
+ */
+ boolean mUserLeaving = false;
+
+ long mInitialStartTime = 0;
+
+ static final int PAUSE_TIMEOUT_MSG = 9;
+ static final int IDLE_TIMEOUT_MSG = 10;
+ static final int IDLE_NOW_MSG = 11;
+ static final int LAUNCH_TIMEOUT_MSG = 16;
+ static final int DESTROY_TIMEOUT_MSG = 17;
+ static final int RESUME_TOP_ACTIVITY_MSG = 19;
+
+ final Handler mHandler = new Handler() {
+ //public Handler() {
+ // if (localLOGV) Slog.v(TAG, "Handler started!");
+ //}
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case PAUSE_TIMEOUT_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ // We don't at this point know if the activity is fullscreen,
+ // so we need to be conservative and assume it isn't.
+ Slog.w(TAG, "Activity pause timeout for " + token);
+ activityPaused(token, null, true);
+ } break;
+ case IDLE_TIMEOUT_MSG: {
+ if (mService.mDidDexOpt) {
+ mService.mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+ nmsg.obj = msg.obj;
+ mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
+ return;
+ }
+ // We don't at this point know if the activity is fullscreen,
+ // so we need to be conservative and assume it isn't.
+ IBinder token = (IBinder)msg.obj;
+ Slog.w(TAG, "Activity idle timeout for " + token);
+ activityIdleInternal(token, true, null);
+ } break;
+ case DESTROY_TIMEOUT_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ // We don't at this point know if the activity is fullscreen,
+ // so we need to be conservative and assume it isn't.
+ Slog.w(TAG, "Activity destroy timeout for " + token);
+ activityDestroyed(token);
+ } break;
+ case IDLE_NOW_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ activityIdleInternal(token, false, null);
+ } break;
+ case LAUNCH_TIMEOUT_MSG: {
+ if (mService.mDidDexOpt) {
+ mService.mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
+ return;
+ }
+ synchronized (mService) {
+ if (mLaunchingActivity.isHeld()) {
+ Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+ mLaunchingActivity.release();
+ }
+ }
+ } break;
+ case RESUME_TOP_ACTIVITY_MSG: {
+ synchronized (mService) {
+ resumeTopActivityLocked(null);
+ }
+ } break;
+ }
+ }
+ };
+
+ ActivityStack(ActivityManagerService service, Context context, boolean mainStack) {
+ mService = service;
+ mContext = context;
+ mMainStack = mainStack;
+ PowerManager pm =
+ (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
+ mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
+ mLaunchingActivity.setReferenceCounted(false);
+ }
+
+ final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && r != notTop) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && !r.delayedResume && r != notTop) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ /**
+ * This is a simplified version of topRunningActivityLocked that provides a number of
+ * optional skip-over modes. It is intended for use with the ActivityController hook only.
+ *
+ * @param token If non-null, any history records matching this token will be skipped.
+ * @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
+ *
+ * @return Returns the HistoryRecord of the next activity on the stack.
+ */
+ final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ // Note: the taskId check depends on real taskId fields being non-zero
+ if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ final int indexOfTokenLocked(IBinder token) {
+ int count = mHistory.size();
+
+ // convert the token to an entry in the history.
+ int index = -1;
+ for (int i=count-1; i>=0; i--) {
+ Object o = mHistory.get(i);
+ if (o == token) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ private final boolean updateLRUListLocked(ActivityRecord r) {
+ final boolean hadit = mLRUActivities.remove(r);
+ mLRUActivities.add(r);
+ return hadit;
+ }
+
+ /**
+ * Returns the top activity in any existing task matching the given
+ * Intent. Returns null if no such task is found.
+ */
+ private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+ ComponentName cls = intent.getComponent();
+ if (info.targetActivity != null) {
+ cls = new ComponentName(info.packageName, info.targetActivity);
+ }
+
+ TaskRecord cp = null;
+
+ final int N = mHistory.size();
+ for (int i=(N-1); i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && r.task != cp
+ && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ cp = r.task;
+ //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
+ // + "/aff=" + r.task.affinity + " to new cls="
+ // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
+ if (r.task.affinity != null) {
+ if (r.task.affinity.equals(info.taskAffinity)) {
+ //Slog.i(TAG, "Found matching affinity!");
+ return r;
+ }
+ } else if (r.task.intent != null
+ && r.task.intent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
+ } else if (r.task.affinityIntent != null
+ && r.task.affinityIntent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the first activity (starting from the top of the stack) that
+ * is the same as the given activity. Returns null if no such activity
+ * is found.
+ */
+ private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
+ ComponentName cls = intent.getComponent();
+ if (info.targetActivity != null) {
+ cls = new ComponentName(info.packageName, info.targetActivity);
+ }
+
+ final int N = mHistory.size();
+ for (int i=(N-1); i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing) {
+ if (r.intent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ final boolean realStartActivityLocked(ActivityRecord r,
+ ProcessRecord app, boolean andResume, boolean checkConfig)
+ throws RemoteException {
+
+ r.startFreezingScreenLocked(app, 0);
+ mService.mWindowManager.setAppVisibility(r, true);
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order. Note that
+ // as a result of this, it can call back into the activity
+ // manager with a new orientation. We don't care about that,
+ // because the activity is not currently running so we are
+ // just restarting it anyway.
+ if (checkConfig) {
+ Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
+ mService.mConfiguration,
+ r.mayFreezeScreenLocked(app) ? r : null);
+ mService.updateConfigurationLocked(config, r);
+ }
+
+ r.app = app;
+
+ if (localLOGV) Slog.v(TAG, "Launching: " + r);
+
+ int idx = app.activities.indexOf(r);
+ if (idx < 0) {
+ app.activities.add(r);
+ }
+ mService.updateLruProcessLocked(app, true, true);
+
+ try {
+ if (app.thread == null) {
+ throw new RemoteException();
+ }
+ List<ResultInfo> results = null;
+ List<Intent> newIntents = null;
+ if (andResume) {
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
+ + " icicle=" + r.icicle
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
+ if (andResume) {
+ EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+ }
+ if (r.isHomeActivity) {
+ mService.mHomeProcess = app;
+ }
+ mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
+ System.identityHashCode(r),
+ r.info, r.icicle, results, newIntents, !andResume,
+ mService.isNextTransitionForward());
+
+ if ((app.info.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
+ // This may be a heavy-weight process! Note that the package
+ // manager will ensure that only activity can run in the main
+ // process of the .apk, which is the only thing that will be
+ // considered heavy-weight.
+ if (app.processName.equals(app.info.packageName)) {
+ if (mService.mHeavyWeightProcess != null
+ && mService.mHeavyWeightProcess != app) {
+ Log.w(TAG, "Starting new heavy weight process " + app
+ + " when already running "
+ + mService.mHeavyWeightProcess);
+ }
+ mService.mHeavyWeightProcess = app;
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
+ msg.obj = r;
+ mService.mHandler.sendMessage(msg);
+ }
+ }
+
+ } catch (RemoteException e) {
+ if (r.launchFailed) {
+ // This is the second time we failed -- finish activity
+ // and give up.
+ Slog.e(TAG, "Second failure launching "
+ + r.intent.getComponent().flattenToShortString()
+ + ", giving up", e);
+ mService.appDiedLocked(app, app.pid, app.thread);
+ requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+ "2nd-crash");
+ return false;
+ }
+
+ // This is the first time we failed -- restart process and
+ // retry.
+ app.activities.remove(r);
+ throw e;
+ }
+
+ r.launchFailed = false;
+ if (updateLRUListLocked(r)) {
+ Slog.w(TAG, "Activity " + r
+ + " being launched, but already in LRU list");
+ }
+
+ if (andResume) {
+ // As part of the process of launching, ActivityThread also performs
+ // a resume.
+ r.state = ActivityState.RESUMED;
+ r.icicle = null;
+ r.haveState = false;
+ r.stopped = false;
+ mResumedActivity = r;
+ r.task.touchActiveTime();
+ completeResumeLocked(r);
+ pauseIfSleepingLocked();
+ } else {
+ // This activity is not starting in the resumed state... which
+ // should look like we asked it to pause+stop (but remain visible),
+ // and it has done so and reported back the current icicle and
+ // other state.
+ r.state = ActivityState.STOPPED;
+ r.stopped = true;
+ }
+
+ // Launch the new version setup screen if needed. We do this -after-
+ // launching the initial activity (that is, home), so that it can have
+ // a chance to initialize itself while in the background, making the
+ // switch back to it faster and look better.
+ if (mMainStack) {
+ mService.startSetupActivityLocked();
+ }
+
+ return true;
+ }
+
+ private final void startSpecificActivityLocked(ActivityRecord r,
+ boolean andResume, boolean checkConfig) {
+ // Is this activity's application already running?
+ ProcessRecord app = mService.getProcessRecordLocked(r.processName,
+ r.info.applicationInfo.uid);
+
+ if (r.startTime == 0) {
+ r.startTime = SystemClock.uptimeMillis();
+ if (mInitialStartTime == 0) {
+ mInitialStartTime = r.startTime;
+ }
+ } else if (mInitialStartTime == 0) {
+ mInitialStartTime = SystemClock.uptimeMillis();
+ }
+
+ if (app != null && app.thread != null) {
+ try {
+ realStartActivityLocked(r, app, andResume, checkConfig);
+ return;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when starting activity "
+ + r.intent.getComponent().flattenToShortString(), e);
+ }
+
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
+
+ mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
+ "activity", r.intent.getComponent(), false);
+ }
+
+ void pauseIfSleepingLocked() {
+ if (mService.mSleeping || mService.mShuttingDown) {
+ if (!mGoingToSleep.isHeld()) {
+ mGoingToSleep.acquire();
+ if (mLaunchingActivity.isHeld()) {
+ mLaunchingActivity.release();
+ mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ }
+ }
+
+ // If we are not currently pausing an activity, get the current
+ // one to pause. If we are pausing one, we will just let that stuff
+ // run and release the wake lock when all done.
+ if (mPausingActivity == null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
+ startPausingLocked(false, true);
+ }
+ }
+ }
+
+ private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+ if (mPausingActivity != null) {
+ RuntimeException e = new RuntimeException();
+ Slog.e(TAG, "Trying to pause when pause is already pending for "
+ + mPausingActivity, e);
+ }
+ ActivityRecord prev = mResumedActivity;
+ if (prev == null) {
+ RuntimeException e = new RuntimeException();
+ Slog.e(TAG, "Trying to pause when nothing is resumed", e);
+ resumeTopActivityLocked(null);
+ return;
+ }
+ if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
+ mResumedActivity = null;
+ mPausingActivity = prev;
+ mLastPausedActivity = prev;
+ prev.state = ActivityState.PAUSING;
+ prev.task.touchActiveTime();
+
+ mService.updateCpuStats();
+
+ if (prev.app != null && prev.app.thread != null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
+ try {
+ EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
+ System.identityHashCode(prev),
+ prev.shortComponentName);
+ prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
+ prev.configChangeFlags);
+ if (mMainStack) {
+ mService.updateUsageStats(prev, false);
+ }
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ }
+ } else {
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ }
+
+ // If we are not going to sleep, we want to ensure the device is
+ // awake until the next activity is started.
+ if (!mService.mSleeping && !mService.mShuttingDown) {
+ mLaunchingActivity.acquire();
+ if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
+ // To be safe, don't allow the wake lock to be held for too long.
+ Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
+ }
+ }
+
+
+ if (mPausingActivity != null) {
+ // Have the window manager pause its key dispatching until the new
+ // activity has started. If we're pausing the activity just because
+ // the screen is being turned off and the UI is sleeping, don't interrupt
+ // key dispatch; the same activity will pick it up again on wakeup.
+ if (!uiSleeping) {
+ prev.pauseKeyDispatchingLocked();
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
+ }
+
+ // Schedule a pause timeout in case the app doesn't respond.
+ // We don't give it much time because this directly impacts the
+ // responsiveness seen by the user.
+ Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+ msg.obj = prev;
+ mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+ if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ } else {
+ // This activity failed to schedule the
+ // pause, so just treat it as being paused now.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
+ resumeTopActivityLocked(null);
+ }
+ }
+
+ final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
+ if (DEBUG_PAUSE) Slog.v(
+ TAG, "Activity paused: token=" + token + ", icicle=" + icicle
+ + ", timeout=" + timeout);
+
+ ActivityRecord r = null;
+
+ synchronized (mService) {
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ r = (ActivityRecord)mHistory.get(index);
+ if (!timeout) {
+ r.icicle = icicle;
+ r.haveState = true;
+ }
+ mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ if (mPausingActivity == r) {
+ r.state = ActivityState.PAUSED;
+ completePauseLocked();
+ } else {
+ EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
+ System.identityHashCode(r), r.shortComponentName,
+ mPausingActivity != null
+ ? mPausingActivity.shortComponentName : "(none)");
+ }
+ }
+ }
+ }
+
+ private final void completePauseLocked() {
+ ActivityRecord prev = mPausingActivity;
+ if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
+
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
+ prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
+ } else if (prev.app != null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
+ if (prev.waitingVisible) {
+ prev.waitingVisible = false;
+ mWaitingVisibleActivities.remove(prev);
+ if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
+ TAG, "Complete pause, no longer waiting: " + prev);
+ }
+ if (prev.configDestroy) {
+ // The previous is being paused because the configuration
+ // is changing, which means it is actually stopping...
+ // To juggle the fact that we are also starting a new
+ // instance right now, we need to first completely stop
+ // the current instance before starting the new one.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
+ destroyActivityLocked(prev, true);
+ } else {
+ mStoppingActivities.add(prev);
+ if (mStoppingActivities.size() > 3) {
+ // If we already have a few activities waiting to stop,
+ // then give up on things going idle and start clearing
+ // them out.
+ if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
+ Message msg = Message.obtain();
+ msg.what = IDLE_NOW_MSG;
+ mHandler.sendMessage(msg);
+ }
+ }
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
+ prev = null;
+ }
+ mPausingActivity = null;
+ }
+
+ if (!mService.mSleeping && !mService.mShuttingDown) {
+ resumeTopActivityLocked(prev);
+ } else {
+ if (mGoingToSleep.isHeld()) {
+ mGoingToSleep.release();
+ }
+ if (mService.mShuttingDown) {
+ mService.notifyAll();
+ }
+ }
+
+ if (prev != null) {
+ prev.resumeKeyDispatchingLocked();
+ }
+
+ if (prev.app != null && prev.cpuTimeAtResume > 0
+ && mService.mBatteryStatsService.isOnBattery()) {
+ long diff = 0;
+ synchronized (mService.mProcessStatsThread) {
+ diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
+ - prev.cpuTimeAtResume;
+ }
+ if (diff > 0) {
+ BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
+ synchronized (bsi) {
+ BatteryStatsImpl.Uid.Proc ps =
+ bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
+ prev.info.packageName);
+ if (ps != null) {
+ ps.addForegroundTimeLocked(diff);
+ }
+ }
+ }
+ }
+ prev.cpuTimeAtResume = 0; // reset it
+ }
+
+ /**
+ * Once we know that we have asked an application to put an activity in
+ * the resumed state (either by launching it or explicitly telling it),
+ * this function updates the rest of our state to match that fact.
+ */
+ private final void completeResumeLocked(ActivityRecord next) {
+ next.idle = false;
+ next.results = null;
+ next.newIntents = null;
+
+ // schedule an idle timeout in case the app doesn't do it for us.
+ Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+ msg.obj = next;
+ mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
+
+ if (false) {
+ // The activity was never told to pause, so just keep
+ // things going as-is. To maintain our own state,
+ // we need to emulate it coming back and saying it is
+ // idle.
+ msg = mHandler.obtainMessage(IDLE_NOW_MSG);
+ msg.obj = next;
+ mHandler.sendMessage(msg);
+ }
+
+ if (mMainStack) {
+ mService.reportResumedActivityLocked(next);
+ }
+
+ next.thumbnail = null;
+ if (mMainStack) {
+ mService.setFocusedActivityLocked(next);
+ }
+ next.resumeKeyDispatchingLocked();
+ ensureActivitiesVisibleLocked(null, 0);
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+
+ // Mark the point when the activity is resuming
+ // TODO: To be more accurate, the mark should be before the onCreate,
+ // not after the onResume. But for subsequent starts, onResume is fine.
+ if (next.app != null) {
+ synchronized (mService.mProcessStatsThread) {
+ next.cpuTimeAtResume = mService.mProcessStats.getCpuTimeForPid(next.app.pid);
+ }
+ } else {
+ next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
+ }
+ }
+
+ /**
+ * Make sure that all activities that need to be visible (that is, they
+ * currently can be seen by the user) actually are.
+ */
+ final void ensureActivitiesVisibleLocked(ActivityRecord top,
+ ActivityRecord starting, String onlyThisProcess, int configChanges) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "ensureActivitiesVisible behind " + top
+ + " configChanges=0x" + Integer.toHexString(configChanges));
+
+ // If the top activity is not fullscreen, then we need to
+ // make sure any activities under it are now visible.
+ final int count = mHistory.size();
+ int i = count-1;
+ while (mHistory.get(i) != top) {
+ i--;
+ }
+ ActivityRecord r;
+ boolean behindFullscreen = false;
+ for (; i>=0; i--) {
+ r = (ActivityRecord)mHistory.get(i);
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Make visible? " + r + " finishing=" + r.finishing
+ + " state=" + r.state);
+ if (r.finishing) {
+ continue;
+ }
+
+ final boolean doThisProcess = onlyThisProcess == null
+ || onlyThisProcess.equals(r.processName);
+
+ // First: if this is not the current activity being started, make
+ // sure it matches the current configuration.
+ if (r != starting && doThisProcess) {
+ ensureActivityConfigurationLocked(r, 0);
+ }
+
+ if (r.app == null || r.app.thread == null) {
+ if (onlyThisProcess == null
+ || onlyThisProcess.equals(r.processName)) {
+ // This activity needs to be visible, but isn't even
+ // running... get it started, but don't resume it
+ // at this point.
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Start and freeze screen for " + r);
+ if (r != starting) {
+ r.startFreezingScreenLocked(r.app, configChanges);
+ }
+ if (!r.visible) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Starting and making visible: " + r);
+ mService.mWindowManager.setAppVisibility(r, true);
+ }
+ if (r != starting) {
+ startSpecificActivityLocked(r, false, false);
+ }
+ }
+
+ } else if (r.visible) {
+ // If this activity is already visible, then there is nothing
+ // else to do here.
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Skipping: already visible at " + r);
+ r.stopFreezingScreenLocked(false);
+
+ } else if (onlyThisProcess == null) {
+ // This activity is not currently visible, but is running.
+ // Tell it to become visible.
+ r.visible = true;
+ if (r.state != ActivityState.RESUMED && r != starting) {
+ // If this activity is paused, tell it
+ // to now show its window.
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Making visible and scheduling visibility: " + r);
+ try {
+ mService.mWindowManager.setAppVisibility(r, true);
+ r.app.thread.scheduleWindowVisibility(r, true);
+ r.stopFreezingScreenLocked(false);
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making visibile: "
+ + r.intent.getComponent(), e);
+ }
+ }
+ }
+
+ // Aggregate current change flags.
+ configChanges |= r.configChangeFlags;
+
+ if (r.fullscreen) {
+ // At this point, nothing else needs to be shown
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Stopping: fullscreen at " + r);
+ behindFullscreen = true;
+ i--;
+ break;
+ }
+ }
+
+ // Now for any activities that aren't visible to the user, make
+ // sure they no longer are keeping the screen frozen.
+ while (i >= 0) {
+ r = (ActivityRecord)mHistory.get(i);
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Make invisible? " + r + " finishing=" + r.finishing
+ + " state=" + r.state
+ + " behindFullscreen=" + behindFullscreen);
+ if (!r.finishing) {
+ if (behindFullscreen) {
+ if (r.visible) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Making invisible: " + r);
+ r.visible = false;
+ try {
+ mService.mWindowManager.setAppVisibility(r, false);
+ if ((r.state == ActivityState.STOPPING
+ || r.state == ActivityState.STOPPED)
+ && r.app != null && r.app.thread != null) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Scheduling invisibility: " + r);
+ r.app.thread.scheduleWindowVisibility(r, false);
+ }
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making hidden: "
+ + r.intent.getComponent(), e);
+ }
+ } else {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Already invisible: " + r);
+ }
+ } else if (r.fullscreen) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Now behindFullscreen: " + r);
+ behindFullscreen = true;
+ }
+ }
+ i--;
+ }
+ }
+
+ /**
+ * Version of ensureActivitiesVisible that can easily be called anywhere.
+ */
+ final void ensureActivitiesVisibleLocked(ActivityRecord starting,
+ int configChanges) {
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ ensureActivitiesVisibleLocked(r, starting, null, configChanges);
+ }
+ }
+
+ /**
+ * Ensure that the top activity in the stack is resumed.
+ *
+ * @param prev The previously resumed activity, for when in the process
+ * of pausing; can be null to call from elsewhere.
+ *
+ * @return Returns true if something is being resumed, or false if
+ * nothing happened.
+ */
+ final boolean resumeTopActivityLocked(ActivityRecord prev) {
+ // Find the first activity that is not finishing.
+ ActivityRecord next = topRunningActivityLocked(null);
+
+ // Remember how we'll process this pause/resume situation, and ensure
+ // that the state is reset however we wind up proceeding.
+ final boolean userLeaving = mUserLeaving;
+ mUserLeaving = false;
+
+ if (next == null) {
+ // There are no more activities! Let's just start up the
+ // Launcher...
+ if (mMainStack) {
+ return mService.startHomeActivityLocked();
+ }
+ }
+
+ next.delayedResume = false;
+
+ // If the top activity is the resumed one, nothing to do.
+ if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return false;
+ }
+
+ // If we are sleeping, and there is no resumed activity, and the top
+ // activity is paused, well that is the state we want.
+ if ((mService.mSleeping || mService.mShuttingDown)
+ && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return false;
+ }
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mStoppingActivities.remove(next);
+ mWaitingVisibleActivities.remove(next);
+
+ if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
+
+ // If we are currently pausing an activity, then don't do anything
+ // until that is done.
+ if (mPausingActivity != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
+ return false;
+ }
+
+ // We need to start pausing the current activity so the top one
+ // can be resumed...
+ if (mResumedActivity != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
+ startPausingLocked(userLeaving, false);
+ return true;
+ }
+
+ if (prev != null && prev != next) {
+ if (!prev.waitingVisible && next != null && !next.nowVisible) {
+ prev.waitingVisible = true;
+ mWaitingVisibleActivities.add(prev);
+ if (DEBUG_SWITCH) Slog.v(
+ TAG, "Resuming top, waiting visible to hide: " + prev);
+ } else {
+ // The next activity is already visible, so hide the previous
+ // activity's windows right now so we can show the new one ASAP.
+ // We only do this if the previous is finishing, which should mean
+ // it is on top of the one being resumed so hiding it quickly
+ // is good. Otherwise, we want to do the normal route of allowing
+ // the resumed activity to be shown so we can decide if the
+ // previous should actually be hidden depending on whether the
+ // new one is found to be full-screen or not.
+ if (prev.finishing) {
+ mService.mWindowManager.setAppVisibility(prev, false);
+ if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
+ + prev + ", waitingVisible="
+ + (prev != null ? prev.waitingVisible : null)
+ + ", nowVisible=" + next.nowVisible);
+ } else {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
+ + prev + ", waitingVisible="
+ + (prev != null ? prev.waitingVisible : null)
+ + ", nowVisible=" + next.nowVisible);
+ }
+ }
+ }
+
+ // We are starting up the next activity, so tell the window manager
+ // that the previous one will be hidden soon. This way it can know
+ // to ignore it when computing the desired screen orientation.
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare close transition: prev=" + prev);
+ if (mNoAnimActivities.contains(prev)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(prev.task == next.task
+ ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
+ : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
+ }
+ mService.mWindowManager.setAppWillBeHidden(prev);
+ mService.mWindowManager.setAppVisibility(prev, false);
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: prev=" + prev);
+ if (mNoAnimActivities.contains(next)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(prev.task == next.task
+ ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
+ : WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ }
+ }
+ if (false) {
+ mService.mWindowManager.setAppWillBeHidden(prev);
+ mService.mWindowManager.setAppVisibility(prev, false);
+ }
+ } else if (mHistory.size() > 1) {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: no previous");
+ if (mNoAnimActivities.contains(next)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ }
+ }
+
+ if (next.app != null && next.app.thread != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
+
+ // This activity is now becoming visible.
+ mService.mWindowManager.setAppVisibility(next, true);
+
+ ActivityRecord lastResumedActivity = mResumedActivity;
+ ActivityState lastState = next.state;
+
+ mService.updateCpuStats();
+
+ next.state = ActivityState.RESUMED;
+ mResumedActivity = next;
+ next.task.touchActiveTime();
+ mService.updateLruProcessLocked(next.app, true, true);
+ updateLRUListLocked(next);
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean updated = false;
+ if (mMainStack) {
+ synchronized (mService) {
+ Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
+ mService.mConfiguration,
+ next.mayFreezeScreenLocked(next.app) ? next : null);
+ if (config != null) {
+ next.frozenBeforeDestroy = true;
+ }
+ updated = mService.updateConfigurationLocked(config, next);
+ }
+ }
+ if (!updated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivityLocked(null);
+ if (DEBUG_SWITCH) Slog.i(TAG,
+ "Activity config changed during resume: " + next
+ + ", new next: " + nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+ }
+ if (mMainStack) {
+ mService.setFocusedActivityLocked(next);
+ }
+ ensureActivitiesVisibleLocked(null, 0);
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return true;
+ }
+
+ try {
+ // Deliver all pending results.
+ ArrayList a = next.results;
+ if (a != null) {
+ final int N = a.size();
+ if (!next.finishing && N > 0) {
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Delivering results to " + next
+ + ": " + a);
+ next.app.thread.scheduleSendResult(next, a);
+ }
+ }
+
+ if (next.newIntents != null) {
+ next.app.thread.scheduleNewIntent(next.newIntents, next);
+ }
+
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
+ System.identityHashCode(next),
+ next.task.taskId, next.shortComponentName);
+
+ next.app.thread.scheduleResumeActivity(next,
+ mService.isNextTransitionForward());
+
+ pauseIfSleepingLocked();
+
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ next.state = lastState;
+ mResumedActivity = lastResumedActivity;
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
+ mService.mWindowManager.setAppStartingWindow(
+ next, next.packageName, next.theme,
+ next.nonLocalizedLabel,
+ next.labelRes, next.icon, null, true);
+ }
+ }
+ startSpecificActivityLocked(next, true, false);
+ return true;
+ }
+
+ // From this point on, if something goes wrong there is no way
+ // to recover the activity.
+ try {
+ next.visible = true;
+ completeResumeLocked(next);
+ } catch (Exception e) {
+ // If any exception gets thrown, toss away this
+ // activity and try the next one.
+ Slog.w(TAG, "Exception thrown during resume of " + next, e);
+ requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
+ "resume-exception");
+ return true;
+ }
+
+ // Didn't need to use the icicle, and it is now out of date.
+ next.icicle = null;
+ next.haveState = false;
+ next.stopped = false;
+
+ } else {
+ // Whoops, need to restart this activity!
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW) {
+ mService.mWindowManager.setAppStartingWindow(
+ next, next.packageName, next.theme,
+ next.nonLocalizedLabel,
+ next.labelRes, next.icon, null, true);
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
+ }
+ startSpecificActivityLocked(next, true, true);
+ }
+
+ return true;
+ }
+
+ private final void startActivityLocked(ActivityRecord r, boolean newTask,
+ boolean doResume) {
+ final int NH = mHistory.size();
+
+ int addPos = -1;
+
+ if (!newTask) {
+ // If starting in an existing task, find where that is...
+ ActivityRecord next = null;
+ boolean startIt = true;
+ for (int i = NH-1; i >= 0; i--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ if (p.finishing) {
+ continue;
+ }
+ if (p.task == r.task) {
+ // Here it is! Now, if this is not yet visible to the
+ // user, then just add it without starting; it will
+ // get started when the user navigates back to it.
+ addPos = i+1;
+ if (!startIt) {
+ mHistory.add(addPos, r);
+ r.inHistory = true;
+ r.task.numActivities++;
+ mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+ r.info.screenOrientation, r.fullscreen);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ return;
+ }
+ break;
+ }
+ if (p.fullscreen) {
+ startIt = false;
+ }
+ next = p;
+ }
+ }
+
+ // Place a new activity at top of stack, so it is next to interact
+ // with the user.
+ if (addPos < 0) {
+ addPos = mHistory.size();
+ }
+
+ // If we are not placing the new activity frontmost, we do not want
+ // to deliver the onUserLeaving callback to the actual frontmost
+ // activity
+ if (addPos < NH) {
+ mUserLeaving = false;
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false");
+ }
+
+ // Slot the activity into the history stack and proceed
+ mHistory.add(addPos, r);
+ r.inHistory = true;
+ r.frontOfTask = newTask;
+ r.task.numActivities++;
+ if (NH > 0) {
+ // We want to show the starting preview window if we are
+ // switching to a new task, or the next activity's process is
+ // not currently running.
+ boolean showStartingIcon = newTask;
+ ProcessRecord proc = r.app;
+ if (proc == null) {
+ proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
+ }
+ if (proc == null || proc.thread == null) {
+ showStartingIcon = true;
+ }
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: starting " + r);
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mNoAnimActivities.add(r);
+ } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ mNoAnimActivities.remove(r);
+ } else {
+ mService.mWindowManager.prepareAppTransition(newTask
+ ? WindowManagerPolicy.TRANSIT_TASK_OPEN
+ : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ mNoAnimActivities.remove(r);
+ }
+ mService.mWindowManager.addAppToken(
+ addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
+ boolean doShow = true;
+ if (newTask) {
+ // Even though this activity is starting fresh, we still need
+ // to reset it to make sure we apply affinities to move any
+ // existing activities from other tasks in to it.
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((r.intent.getFlags()
+ &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ resetTaskIfNeededLocked(r, r);
+ doShow = topRunningNonDelayedActivityLocked(null) == r;
+ }
+ }
+ if (SHOW_APP_STARTING_PREVIEW && doShow) {
+ // Figure out if we are transitioning from another activity that is
+ // "has the same starting icon" as the next one. This allows the
+ // window manager to keep the previous window it had previously
+ // created, if it still had one.
+ ActivityRecord prev = mResumedActivity;
+ if (prev != null) {
+ // We don't want to reuse the previous starting preview if:
+ // (1) The current activity is in a different task.
+ if (prev.task != r.task) prev = null;
+ // (2) The current activity is already displayed.
+ else if (prev.nowVisible) prev = null;
+ }
+ mService.mWindowManager.setAppStartingWindow(
+ r, r.packageName, r.theme, r.nonLocalizedLabel,
+ r.labelRes, r.icon, prev, showStartingIcon);
+ }
+ } else {
+ // If this is the first activity, don't do any fancy animations,
+ // because there is nothing for it to animate on top of.
+ mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+ r.info.screenOrientation, r.fullscreen);
+ }
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ }
+
+ /**
+ * Perform a reset of the given task, if needed as part of launching it.
+ * Returns the new HistoryRecord at the top of the task.
+ */
+ private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
+ ActivityRecord newActivity) {
+ boolean forceReset = (newActivity.info.flags
+ &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
+ if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+ if ((newActivity.info.flags
+ &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
+ forceReset = true;
+ }
+ }
+
+ final TaskRecord task = taskTop.task;
+
+ // We are going to move through the history list so that we can look
+ // at each activity 'target' with 'below' either the interesting
+ // activity immediately below it in the stack or null.
+ ActivityRecord target = null;
+ int targetI = 0;
+ int taskTopI = -1;
+ int replyChainEnd = -1;
+ int lastReparentPos = -1;
+ for (int i=mHistory.size()-1; i>=-1; i--) {
+ ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
+
+ if (below != null && below.finishing) {
+ continue;
+ }
+ if (target == null) {
+ target = below;
+ targetI = i;
+ // If we were in the middle of a reply chain before this
+ // task, it doesn't appear like the root of the chain wants
+ // anything interesting, so drop it.
+ replyChainEnd = -1;
+ continue;
+ }
+
+ final int flags = target.info.flags;
+
+ final boolean finishOnTaskLaunch =
+ (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
+ final boolean allowTaskReparenting =
+ (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
+
+ if (target.task == task) {
+ // We are inside of the task being reset... we'll either
+ // finish this activity, push it out for another task,
+ // or leave it as-is. We only do this
+ // for activities that are not the root of the task (since
+ // if we finish the root, we may no longer have the task!).
+ if (taskTopI < 0) {
+ taskTopI = targetI;
+ }
+ if (below != null && below.task == task) {
+ final boolean clearWhenTaskReset =
+ (target.intent.getFlags()
+ &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
+ if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) {
+ // If this activity is sending a reply to a previous
+ // activity, we can't do anything with it now until
+ // we reach the start of the reply chain.
+ // XXX note that we are assuming the result is always
+ // to the previous activity, which is almost always
+ // the case but we really shouldn't count on.
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting
+ && target.taskAffinity != null
+ && !target.taskAffinity.equals(task.affinity)) {
+ // If this activity has an affinity for another
+ // task, then we need to move it out of here. We will
+ // move it as far out of the way as possible, to the
+ // bottom of the activity stack. This also keeps it
+ // correctly ordered with any activities we previously
+ // moved.
+ ActivityRecord p = (ActivityRecord)mHistory.get(0);
+ if (target.taskAffinity != null
+ && target.taskAffinity.equals(p.task.affinity)) {
+ // If the activity currently at the bottom has the
+ // same task affinity as the one we are moving,
+ // then merge it into the same task.
+ target.task = p.task;
+ if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ + " out to bottom task " + p.task);
+ } else {
+ mService.mCurTask++;
+ if (mService.mCurTask <= 0) {
+ mService.mCurTask = 1;
+ }
+ target.task = new TaskRecord(mService.mCurTask, target.info, null,
+ (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ target.task.affinityIntent = target.intent;
+ if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ + " out to new task " + target.task);
+ }
+ mService.mWindowManager.setAppGroupId(target, task.taskId);
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ int dstPos = 0;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+ + " out to target's task " + target.task);
+ task.numActivities--;
+ p.task = target.task;
+ target.task.numActivities++;
+ mHistory.remove(srcPos);
+ mHistory.add(dstPos, p);
+ mService.mWindowManager.moveAppToken(dstPos, p);
+ mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+ dstPos++;
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ i++;
+ }
+ if (taskTop == p) {
+ taskTop = below;
+ }
+ if (taskTopI == replyChainEnd) {
+ taskTopI = -1;
+ }
+ replyChainEnd = -1;
+ if (mMainStack) {
+ mService.addRecentTaskLocked(target.task);
+ }
+ } else if (forceReset || finishOnTaskLaunch
+ || clearWhenTaskReset) {
+ // If the activity should just be removed -- either
+ // because it asks for it, or the task should be
+ // cleared -- then finish it and anything that is
+ // part of its reply chain.
+ if (clearWhenTaskReset) {
+ // In this case, we want to finish this activity
+ // and everything above it, so be sneaky and pretend
+ // like these are all in the reply chain.
+ replyChainEnd = targetI+1;
+ while (replyChainEnd < mHistory.size() &&
+ ((ActivityRecord)mHistory.get(
+ replyChainEnd)).task == task) {
+ replyChainEnd++;
+ }
+ replyChainEnd--;
+ } else if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ ActivityRecord p = null;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(p, srcPos,
+ Activity.RESULT_CANCELED, null, "reset")) {
+ replyChainEnd--;
+ srcPos--;
+ }
+ }
+ if (taskTop == p) {
+ taskTop = below;
+ }
+ if (taskTopI == replyChainEnd) {
+ taskTopI = -1;
+ }
+ replyChainEnd = -1;
+ } else {
+ // If we were in the middle of a chain, well the
+ // activity that started it all doesn't want anything
+ // special, so leave it all as-is.
+ replyChainEnd = -1;
+ }
+ } else {
+ // Reached the bottom of the task -- any reply chain
+ // should be left as-is.
+ replyChainEnd = -1;
+ }
+
+ } else if (target.resultTo != null) {
+ // If this activity is sending a reply to a previous
+ // activity, we can't do anything with it now until
+ // we reach the start of the reply chain.
+ // XXX note that we are assuming the result is always
+ // to the previous activity, which is almost always
+ // the case but we really shouldn't count on.
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+
+ } else if (taskTopI >= 0 && allowTaskReparenting
+ && task.affinity != null
+ && task.affinity.equals(target.taskAffinity)) {
+ // We are inside of another task... if this activity has
+ // an affinity for our task, then either remove it if we are
+ // clearing or move it over to our task. Note that
+ // we currently punt on the case where we are resetting a
+ // task that is not at the top but who has activities above
+ // with an affinity to it... this is really not a normal
+ // case, and we will need to later pull that task to the front
+ // and usually at that point we will do the reset and pick
+ // up those remaining activities. (This only happens if
+ // someone starts an activity in a new task from an activity
+ // in a task that is not currently on top.)
+ if (forceReset || finishOnTaskLaunch) {
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ ActivityRecord p = null;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(p, srcPos,
+ Activity.RESULT_CANCELED, null, "reset")) {
+ taskTopI--;
+ lastReparentPos--;
+ replyChainEnd--;
+ srcPos--;
+ }
+ }
+ replyChainEnd = -1;
+ } else {
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (lastReparentPos < 0) {
+ lastReparentPos = taskTopI;
+ taskTop = p;
+ } else {
+ lastReparentPos--;
+ }
+ mHistory.remove(srcPos);
+ p.task.numActivities--;
+ p.task = task;
+ mHistory.add(lastReparentPos, p);
+ if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+ + " in to resetting task " + task);
+ task.numActivities++;
+ mService.mWindowManager.moveAppToken(lastReparentPos, p);
+ mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ }
+ replyChainEnd = -1;
+
+ // Now we've moved it in to place... but what if this is
+ // a singleTop activity and we have put it on top of another
+ // instance of the same activity? Then we drop the instance
+ // below so it remains singleTop.
+ if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
+ for (int j=lastReparentPos-1; j>=0; j--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(j);
+ if (p.finishing) {
+ continue;
+ }
+ if (p.intent.getComponent().equals(target.intent.getComponent())) {
+ if (finishActivityLocked(p, j,
+ Activity.RESULT_CANCELED, null, "replace")) {
+ taskTopI--;
+ lastReparentPos--;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ target = below;
+ targetI = i;
+ }
+
+ return taskTop;
+ }
+
+ /**
+ * Perform clear operation as requested by
+ * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
+ * stack to the given task, then look for
+ * an instance of that activity in the stack and, if found, finish all
+ * activities on top of it and return the instance.
+ *
+ * @param newR Description of the new activity being started.
+ * @return Returns the old activity that should be continue to be used,
+ * or null if none was found.
+ */
+ private final ActivityRecord performClearTaskLocked(int taskId,
+ ActivityRecord newR, int launchFlags, boolean doClear) {
+ int i = mHistory.size();
+
+ // First find the requested task.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId == taskId) {
+ i++;
+ break;
+ }
+ }
+
+ // Now clear it.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.task.taskId != taskId) {
+ return null;
+ }
+ if (r.realActivity.equals(newR.realActivity)) {
+ // Here it is! Now finish everything in front...
+ ActivityRecord ret = r;
+ if (doClear) {
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i--;
+ }
+ }
+ }
+
+ // Finally, if this is a normal launch mode (that is, not
+ // expecting onNewIntent()), then we will finish the current
+ // instance of the activity so a new fresh one can be started.
+ if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+ && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
+ if (!ret.finishing) {
+ int index = indexOfTokenLocked(ret);
+ if (index >= 0) {
+ finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
+ null, "clear");
+ }
+ return null;
+ }
+ }
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Find the activity in the history stack within the given task. Returns
+ * the index within the history at which it's found, or < 0 if not found.
+ */
+ private final int findActivityInHistoryLocked(ActivityRecord r, int task) {
+ int i = mHistory.size();
+ while (i > 0) {
+ i--;
+ ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
+ if (candidate.task.taskId != task) {
+ break;
+ }
+ if (candidate.realActivity.equals(r.realActivity)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Reorder the history stack so that the activity at the given index is
+ * brought to the front.
+ */
+ private final ActivityRecord moveActivityToFrontLocked(int where) {
+ ActivityRecord newTop = (ActivityRecord)mHistory.remove(where);
+ int top = mHistory.size();
+ ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
+ mHistory.add(top, newTop);
+ oldTop.frontOfTask = false;
+ newTop.frontOfTask = true;
+ return newTop;
+ }
+
+ final int startActivityLocked(IApplicationThread caller,
+ Intent intent, String resolvedType,
+ Uri[] grantedUriPermissions,
+ int grantedMode, ActivityInfo aInfo, IBinder resultTo,
+ String resultWho, int requestCode,
+ int callingPid, int callingUid, boolean onlyIfNeeded,
+ boolean componentSpecified) {
+ Slog.i(TAG, "Starting activity: " + intent);
+
+ ActivityRecord sourceRecord = null;
+ ActivityRecord resultRecord = null;
+ if (resultTo != null) {
+ int index = indexOfTokenLocked(resultTo);
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Sending result to " + resultTo + " (index " + index + ")");
+ if (index >= 0) {
+ sourceRecord = (ActivityRecord)mHistory.get(index);
+ if (requestCode >= 0 && !sourceRecord.finishing) {
+ resultRecord = sourceRecord;
+ }
+ }
+ }
+
+ int launchFlags = intent.getFlags();
+
+ if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
+ && sourceRecord != null) {
+ // Transfer the result target from the source activity to the new
+ // one being started, including any failures.
+ if (requestCode >= 0) {
+ return START_FORWARD_AND_REQUEST_CONFLICT;
+ }
+ resultRecord = sourceRecord.resultTo;
+ resultWho = sourceRecord.resultWho;
+ requestCode = sourceRecord.requestCode;
+ sourceRecord.resultTo = null;
+ if (resultRecord != null) {
+ resultRecord.removeResultsLocked(
+ sourceRecord, resultWho, requestCode);
+ }
+ }
+
+ int err = START_SUCCESS;
+
+ if (intent.getComponent() == null) {
+ // We couldn't find a class that can handle the given Intent.
+ // That's the end of that!
+ err = START_INTENT_NOT_RESOLVED;
+ }
+
+ if (err == START_SUCCESS && aInfo == null) {
+ // We couldn't find the specific class specified in the Intent.
+ // Also the end of the line.
+ err = START_CLASS_NOT_FOUND;
+ }
+
+ ProcessRecord callerApp = null;
+ if (err == START_SUCCESS && caller != null) {
+ callerApp = mService.getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ callingPid = callerApp.pid;
+ callingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + callingPid + ") when starting: "
+ + intent.toString());
+ err = START_PERMISSION_DENIED;
+ }
+ }
+
+ if (err != START_SUCCESS) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ return err;
+ }
+
+ final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,
+ callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + aInfo.permission;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ if (mMainStack) {
+ if (mService.mController != null) {
+ boolean abort = false;
+ try {
+ // The Intent we give to the watcher has the extra data
+ // stripped off, since it can contain private information.
+ Intent watchIntent = intent.cloneFilter();
+ abort = !mService.mController.activityStarting(watchIntent,
+ aInfo.applicationInfo.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ }
+
+ if (abort) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ // We pretend to the caller that it was really started, but
+ // they will just get a cancel result.
+ return START_SUCCESS;
+ }
+ }
+ }
+
+ ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
+ intent, resolvedType, aInfo, mService.mConfiguration,
+ resultRecord, resultWho, requestCode, componentSpecified);
+
+ if (mMainStack) {
+ if (mResumedActivity == null
+ || mResumedActivity.info.applicationInfo.uid != callingUid) {
+ if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+ PendingActivityLaunch pal = new PendingActivityLaunch();
+ pal.r = r;
+ pal.sourceRecord = sourceRecord;
+ pal.grantedUriPermissions = grantedUriPermissions;
+ pal.grantedMode = grantedMode;
+ pal.onlyIfNeeded = onlyIfNeeded;
+ mService.mPendingActivityLaunches.add(pal);
+ return START_SWITCHES_CANCELED;
+ }
+ }
+
+ if (mService.mDidAppSwitch) {
+ // This is the second allowed switch since we stopped switches,
+ // so now just generally allow switches. Use case: user presses
+ // home (switches disabled, switch to home, mDidAppSwitch now true);
+ // user taps a home icon (coming from home so allowed, we hit here
+ // and now allow anyone to switch again).
+ mService.mAppSwitchesAllowedTime = 0;
+ } else {
+ mService.mDidAppSwitch = true;
+ }
+
+ mService.doPendingActivityLaunchesLocked(false);
+ }
+
+ return startActivityUncheckedLocked(r, sourceRecord,
+ grantedUriPermissions, grantedMode, onlyIfNeeded, true);
+ }
+
+ final int startActivityUncheckedLocked(ActivityRecord r,
+ ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
+ int grantedMode, boolean onlyIfNeeded, boolean doResume) {
+ final Intent intent = r.intent;
+ final int callingUid = r.launchedFromUid;
+
+ int launchFlags = intent.getFlags();
+
+ // We'll invoke onUserLeaving before onPause only if the launching
+ // activity did not explicitly state that this is an automated launch.
+ mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+ if (DEBUG_USER_LEAVING) Slog.v(TAG,
+ "startActivity() => mUserLeaving=" + mUserLeaving);
+
+ // If the caller has asked not to resume at this point, we make note
+ // of this in the record so that we can skip it when trying to find
+ // the top running activity.
+ if (!doResume) {
+ r.delayedResume = true;
+ }
+
+ ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
+ != 0 ? r : null;
+
+ // If the onlyIfNeeded flag is set, then we can do this if the activity
+ // being launched is the same as the one making the call... or, as
+ // a special case, if we do not know the caller then we count the
+ // current top activity as the caller.
+ if (onlyIfNeeded) {
+ ActivityRecord checkedCaller = sourceRecord;
+ if (checkedCaller == null) {
+ checkedCaller = topRunningNonDelayedActivityLocked(notTop);
+ }
+ if (!checkedCaller.realActivity.equals(r.realActivity)) {
+ // Caller is not the same as launcher, so always needed.
+ onlyIfNeeded = false;
+ }
+ }
+
+ if (sourceRecord == null) {
+ // This activity is not being started from another... in this
+ // case we -always- start a new task.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
+ + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // The original activity who is starting us is running as a single
+ // instance... this new activity it is starting must go on its
+ // own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+ // The activity being started is a single instance... it always
+ // gets launched into its own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+
+ if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ // For whatever reason this activity is being launched into a new
+ // task... yet the caller has requested a result back. Well, that
+ // is pretty messed up, so instead immediately send back a cancel
+ // and let the new task continue launched as normal without a
+ // dependency on its originator.
+ Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+ sendActivityResultLocked(-1,
+ r.resultTo, r.resultWho, r.requestCode,
+ Activity.RESULT_CANCELED, null);
+ r.resultTo = null;
+ }
+
+ boolean addingToTask = false;
+ if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
+ (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // If bring to front is requested, and no result is requested, and
+ // we can find a task that was started with this same
+ // component, then instead of launching bring that one to the front.
+ if (r.resultTo == null) {
+ // See if there is a task to bring to the front. If this is
+ // a SINGLE_INSTANCE activity, there can be one and only one
+ // instance of it in the history, and it is always in its own
+ // unique task, so we do a special search.
+ ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
+ ? findTaskLocked(intent, r.info)
+ : findActivityLocked(intent, r.info);
+ if (taskTop != null) {
+ if (taskTop.task.intent == null) {
+ // This task was started because of movement of
+ // the activity based on affinity... now that we
+ // are actually launching it, we can assign the
+ // base intent.
+ taskTop.task.setIntent(intent, r.info);
+ }
+ // If the target task is not in the front, then we need
+ // to bring it to the front... except... well, with
+ // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
+ // to have the same behavior as if a new instance was
+ // being started, which means not bringing it to the front
+ // if the caller is not itself in the front.
+ ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
+ if (curTop.task != taskTop.task) {
+ r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+ boolean callerAtFront = sourceRecord == null
+ || curTop.task == sourceRecord.task;
+ if (callerAtFront) {
+ // We really do want to push this one into the
+ // user's face, right now.
+ moveTaskToFrontLocked(taskTop.task, r);
+ }
+ }
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ taskTop = resetTaskIfNeededLocked(taskTop, r);
+ }
+ if (onlyIfNeeded) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it! And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return START_RETURN_INTENT_TO_CALLER;
+ }
+ if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // In this situation we want to remove all activities
+ // from the task up to the one being started. In most
+ // cases this means we are resetting the task to its
+ // initial state.
+ ActivityRecord top = performClearTaskLocked(
+ taskTop.task.taskId, r, launchFlags, true);
+ if (top != null) {
+ if (top.frontOfTask) {
+ // Activity aliases may mean we use different
+ // intents for the top activity, so make sure
+ // the task now has the identity of the new
+ // intent.
+ top.task.setIntent(r.intent, r.info);
+ }
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ } else {
+ // A special case: we need to
+ // start the activity because it is not currently
+ // running, and the caller has asked to clear the
+ // current task to have this activity at the top.
+ addingToTask = true;
+ // Now pretend like this activity is being started
+ // by the top of its task, so it is put in the
+ // right place.
+ sourceRecord = taskTop;
+ }
+ } else if (r.realActivity.equals(taskTop.task.realActivity)) {
+ // In this case the top activity on the task is the
+ // same as the one being launched, so we take that
+ // as a request to bring the task to the foreground.
+ // If the top activity in the task is the root
+ // activity, deliver this new intent to it if it
+ // desires.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ && taskTop.realActivity.equals(r.realActivity)) {
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
+ if (taskTop.frontOfTask) {
+ taskTop.task.setIntent(r.intent, r.info);
+ }
+ taskTop.deliverNewIntentLocked(callingUid, r.intent);
+ } else if (!r.intent.filterEquals(taskTop.task.intent)) {
+ // In this case we are launching the root activity
+ // of the task, but with a different intent. We
+ // should start a new instance on top.
+ addingToTask = true;
+ sourceRecord = taskTop;
+ }
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+ // In this case an activity is being launched in to an
+ // existing task, without resetting that task. This
+ // is typically the situation of launching an activity
+ // from a notification or shortcut. We want to place
+ // the new activity on top of the current task.
+ addingToTask = true;
+ sourceRecord = taskTop;
+ } else if (!taskTop.task.rootWasReset) {
+ // In this case we are launching in to an existing task
+ // that has not yet been started from its front door.
+ // The current task has been brought to the front.
+ // Ideally, we'd probably like to place this new task
+ // at the bottom of its stack, but that's a little hard
+ // to do with the current organization of the code so
+ // for now we'll just drop it.
+ taskTop.task.setIntent(r.intent, r.info);
+ }
+ if (!addingToTask) {
+ // We didn't do anything... but it was needed (a.k.a., client
+ // don't use that intent!) And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return START_TASK_TO_FRONT;
+ }
+ }
+ }
+ }
+
+ //String uri = r.intent.toURI();
+ //Intent intent2 = new Intent(uri);
+ //Slog.i(TAG, "Given intent: " + r.intent);
+ //Slog.i(TAG, "URI is: " + uri);
+ //Slog.i(TAG, "To intent: " + intent2);
+
+ if (r.packageName != null) {
+ // If the activity being launched is the same as the one currently
+ // at the top, then we need to check if it should only be launched
+ // once.
+ ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
+ if (top != null && r.resultTo == null) {
+ if (top.realActivity.equals(r.realActivity)) {
+ if (top.app != null && top.app.thread != null) {
+ if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+ logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ if (onlyIfNeeded) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it!
+ return START_RETURN_INTENT_TO_CALLER;
+ }
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ return START_DELIVERED_TO_TOP;
+ }
+ }
+ }
+ }
+
+ } else {
+ if (r.resultTo != null) {
+ sendActivityResultLocked(-1,
+ r.resultTo, r.resultWho, r.requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ return START_CLASS_NOT_FOUND;
+ }
+
+ boolean newTask = false;
+
+ // Should this be considered a new task?
+ if (r.resultTo == null && !addingToTask
+ && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ // todo: should do better management of integers.
+ mService.mCurTask++;
+ if (mService.mCurTask <= 0) {
+ mService.mCurTask = 1;
+ }
+ r.task = new TaskRecord(mService.mCurTask, r.info, intent,
+ (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new task " + r.task);
+ newTask = true;
+ if (mMainStack) {
+ mService.addRecentTaskLocked(r.task);
+ }
+
+ } else if (sourceRecord != null) {
+ if (!addingToTask &&
+ (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+ // In this case, we are adding the activity to an existing
+ // task, but the caller has asked to clear that task if the
+ // activity is already running.
+ ActivityRecord top = performClearTaskLocked(
+ sourceRecord.task.taskId, r, launchFlags, true);
+ if (top != null) {
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return START_DELIVERED_TO_TOP;
+ }
+ } else if (!addingToTask &&
+ (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+ // In this case, we are launching an activity in our own task
+ // that may already be running somewhere in the history, and
+ // we want to shuffle it to the front of the stack if so.
+ int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
+ if (where >= 0) {
+ ActivityRecord top = moveActivityToFrontLocked(where);
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return START_DELIVERED_TO_TOP;
+ }
+ }
+ // An existing activity is starting this new activity, so we want
+ // to keep the new one in the same task as the one that is starting
+ // it.
+ r.task = sourceRecord.task;
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in existing task " + r.task);
+
+ } else {
+ // This not being started from an existing activity, and not part
+ // of a new task... just put it in the top task, though these days
+ // this case should never happen.
+ final int N = mHistory.size();
+ ActivityRecord prev =
+ N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
+ r.task = prev != null
+ ? prev.task
+ : new TaskRecord(mService.mCurTask, r.info, intent,
+ (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new guessed " + r.task);
+ }
+
+ if (grantedUriPermissions != null && callingUid > 0) {
+ for (int i=0; i<grantedUriPermissions.length; i++) {
+ mService.grantUriPermissionLocked(callingUid, r.packageName,
+ grantedUriPermissions[i], grantedMode, r.getUriPermissionsLocked());
+ }
+ }
+
+ mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ intent, r.getUriPermissionsLocked());
+
+ if (newTask) {
+ EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
+ }
+ logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
+ startActivityLocked(r, newTask, doResume);
+ return START_SUCCESS;
+ }
+
+ final int startActivityMayWait(IApplicationThread caller,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded,
+ boolean debug, WaitResult outResult, Configuration config) {
+ // 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!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, resolvedType,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+
+ if (aInfo != null) {
+ // Store the found target back into the intent, because now that
+ // we have it we never want to do this again. For example, if the
+ // user navigates back to this point in the history, we should
+ // always restart the exact same activity.
+ intent.setComponent(new ComponentName(
+ aInfo.applicationInfo.packageName, aInfo.name));
+
+ // Don't debug things in the system process
+ if (debug) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setDebugApp(aInfo.processName, true, false);
+ }
+ }
+ }
+
+ synchronized (mService) {
+ int callingPid;
+ int callingUid;
+ if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+
+ mConfigWillChange = config != null
+ && mService.mConfiguration.diff(config) != 0;
+ if (DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Starting activity when config will change = " + mConfigWillChange);
+
+ final long origId = Binder.clearCallingIdentity();
+
+ if (mMainStack && aInfo != null &&
+ (aInfo.applicationInfo.flags&ApplicationInfo.CANT_SAVE_STATE) != 0) {
+ // This may be a heavy-weight process! Check to see if we already
+ // have another, different heavy-weight process running.
+ if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+ if (mService.mHeavyWeightProcess != null &&
+ (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
+ !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
+ int realCallingPid = callingPid;
+ int realCallingUid = callingUid;
+ if (caller != null) {
+ ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ realCallingPid = callerApp.pid;
+ realCallingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + realCallingPid + ") when starting: "
+ + intent.toString());
+ return START_PERMISSION_DENIED;
+ }
+ }
+
+ IIntentSender target = mService.getIntentSenderLocked(
+ IActivityManager.INTENT_SENDER_ACTIVITY, "android",
+ realCallingUid, null, null, 0, intent,
+ resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT);
+
+ Intent newIntent = new Intent();
+ if (requestCode >= 0) {
+ // Caller is requesting a result.
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+ new IntentSender(target));
+ if (mService.mHeavyWeightProcess.activities.size() > 0) {
+ ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
+ hist.packageName);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
+ hist.task.taskId);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+ aInfo.packageName);
+ newIntent.setFlags(intent.getFlags());
+ newIntent.setClassName("android",
+ HeavyWeightSwitcherActivity.class.getName());
+ intent = newIntent;
+ resolvedType = null;
+ caller = null;
+ callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
+ componentSpecified = true;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, null,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+ }
+ }
+ }
+
+ int res = startActivityLocked(caller, intent, resolvedType,
+ grantedUriPermissions, grantedMode, aInfo,
+ resultTo, resultWho, requestCode, callingPid, callingUid,
+ onlyIfNeeded, componentSpecified);
+
+ if (mConfigWillChange && mMainStack) {
+ // If the caller also wants to switch to a new configuration,
+ // do so now. This allows a clean switch, as we are waiting
+ // for the current activity to pause (so we will not destroy
+ // it), and have not yet started the next activity.
+ mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+ "updateConfiguration()");
+ mConfigWillChange = false;
+ if (DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Updating to new configuration after starting activity.");
+ mService.updateConfigurationLocked(config, null);
+ }
+
+ Binder.restoreCallingIdentity(origId);
+
+ if (outResult != null) {
+ outResult.result = res;
+ if (res == IActivityManager.START_SUCCESS) {
+ mWaitingActivityLaunched.add(outResult);
+ do {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ } else if (res == IActivityManager.START_TASK_TO_FRONT) {
+ ActivityRecord r = this.topRunningActivityLocked(null);
+ if (r.nowVisible) {
+ outResult.timeout = false;
+ outResult.who = new ComponentName(r.info.packageName, r.info.name);
+ outResult.totalTime = 0;
+ outResult.thisTime = 0;
+ } else {
+ outResult.thisTime = SystemClock.uptimeMillis();
+ mWaitingActivityVisible.add(outResult);
+ do {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ }
+ }
+ }
+
+ return res;
+ }
+ }
+
+ void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
+ long thisTime, long totalTime) {
+ for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
+ WaitResult w = mWaitingActivityLaunched.get(i);
+ w.timeout = timeout;
+ if (r != null) {
+ w.who = new ComponentName(r.info.packageName, r.info.name);
+ }
+ w.thisTime = thisTime;
+ w.totalTime = totalTime;
+ }
+ mService.notifyAll();
+ }
+
+ void reportActivityVisibleLocked(ActivityRecord r) {
+ for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
+ WaitResult w = mWaitingActivityVisible.get(i);
+ w.timeout = false;
+ if (r != null) {
+ w.who = new ComponentName(r.info.packageName, r.info.name);
+ }
+ w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
+ w.thisTime = w.totalTime;
+ }
+ mService.notifyAll();
+ }
+
+ void sendActivityResultLocked(int callingUid, ActivityRecord r,
+ String resultWho, int requestCode, int resultCode, Intent data) {
+
+ if (callingUid > 0) {
+ mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ data, r.getUriPermissionsLocked());
+ }
+
+ if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+ + " : who=" + resultWho + " req=" + requestCode
+ + " res=" + resultCode + " data=" + data);
+ if (mResumedActivity == r && r.app != null && r.app.thread != null) {
+ try {
+ ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+ list.add(new ResultInfo(resultWho, requestCode,
+ resultCode, data));
+ r.app.thread.scheduleSendResult(r, list);
+ return;
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception thrown sending result to " + r, e);
+ }
+ }
+
+ r.addResultLocked(null, resultWho, requestCode, resultCode, data);
+ }
+
+ private final void stopActivityLocked(ActivityRecord r) {
+ if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+ || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
+ if (!r.finishing) {
+ requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+ "no-history");
+ }
+ } else if (r.app != null && r.app.thread != null) {
+ if (mMainStack) {
+ if (mService.mFocusedActivity == r) {
+ mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+ }
+ }
+ r.resumeKeyDispatchingLocked();
+ try {
+ r.stopped = false;
+ r.state = ActivityState.STOPPING;
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Stopping visible=" + r.visible + " for " + r);
+ if (!r.visible) {
+ mService.mWindowManager.setAppVisibility(r, false);
+ }
+ r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
+ } catch (Exception e) {
+ // Maybe just ignore exceptions here... if the process
+ // has crashed, our death notification will clean things
+ // up.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ // Just in case, assume it to be stopped.
+ r.stopped = true;
+ r.state = ActivityState.STOPPED;
+ if (r.configDestroy) {
+ destroyActivityLocked(r, true);
+ }
+ }
+ }
+ }
+
+ final ArrayList<ActivityRecord> processStoppingActivitiesLocked(
+ boolean remove) {
+ int N = mStoppingActivities.size();
+ if (N <= 0) return null;
+
+ ArrayList<ActivityRecord> stops = null;
+
+ final boolean nowVisible = mResumedActivity != null
+ && mResumedActivity.nowVisible
+ && !mResumedActivity.waitingVisible;
+ for (int i=0; i<N; i++) {
+ ActivityRecord s = mStoppingActivities.get(i);
+ if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
+ + nowVisible + " waitingVisible=" + s.waitingVisible
+ + " finishing=" + s.finishing);
+ if (s.waitingVisible && nowVisible) {
+ mWaitingVisibleActivities.remove(s);
+ s.waitingVisible = false;
+ if (s.finishing) {
+ // If this activity is finishing, it is sitting on top of
+ // everyone else but we now know it is no longer needed...
+ // so get rid of it. Otherwise, we need to go through the
+ // normal flow and hide it once we determine that it is
+ // hidden by the activities in front of it.
+ if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
+ mService.mWindowManager.setAppVisibility(s, false);
+ }
+ }
+ if (!s.waitingVisible && remove) {
+ if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
+ if (stops == null) {
+ stops = new ArrayList<ActivityRecord>();
+ }
+ stops.add(s);
+ mStoppingActivities.remove(i);
+ N--;
+ i--;
+ }
+ }
+
+ return stops;
+ }
+
+ final void activityIdleInternal(IBinder token, boolean fromTimeout,
+ Configuration config) {
+ if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
+
+ ArrayList<ActivityRecord> stops = null;
+ ArrayList<ActivityRecord> finishes = null;
+ ArrayList<ActivityRecord> thumbnails = null;
+ int NS = 0;
+ int NF = 0;
+ int NT = 0;
+ IApplicationThread sendThumbnail = null;
+ boolean booting = false;
+ boolean enableScreen = false;
+
+ synchronized (mService) {
+ if (token != null) {
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
+ }
+
+ // Get the activity record.
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+
+ if (fromTimeout) {
+ reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
+ }
+
+ // This is a hack to semi-deal with a race condition
+ // in the client where it can be constructed with a
+ // newer configuration from when we asked it to launch.
+ // We'll update with whatever configuration it now says
+ // it used to launch.
+ if (config != null) {
+ r.configuration = config;
+ }
+
+ // No longer need to keep the device awake.
+ if (mResumedActivity == r && mLaunchingActivity.isHeld()) {
+ mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ mLaunchingActivity.release();
+ }
+
+ // We are now idle. If someone is waiting for a thumbnail from
+ // us, we can now deliver.
+ r.idle = true;
+ mService.scheduleAppGcsLocked();
+ if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
+ sendThumbnail = r.app.thread;
+ r.thumbnailNeeded = false;
+ }
+
+ // If this activity is fullscreen, set up to hide those under it.
+
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
+ ensureActivitiesVisibleLocked(null, 0);
+
+ //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
+ if (mMainStack) {
+ if (!mService.mBooted && !fromTimeout) {
+ mService.mBooted = true;
+ enableScreen = true;
+ }
+ }
+
+ } else if (fromTimeout) {
+ reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
+ }
+
+ // Atomically retrieve all of the other things to do.
+ stops = processStoppingActivitiesLocked(true);
+ NS = stops != null ? stops.size() : 0;
+ if ((NF=mFinishingActivities.size()) > 0) {
+ finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
+ mFinishingActivities.clear();
+ }
+ if ((NT=mService.mCancelledThumbnails.size()) > 0) {
+ thumbnails = new ArrayList<ActivityRecord>(mService.mCancelledThumbnails);
+ mService.mCancelledThumbnails.clear();
+ }
+
+ if (mMainStack) {
+ booting = mService.mBooting;
+ mService.mBooting = false;
+ }
+ }
+
+ int i;
+
+ // Send thumbnail if requested.
+ if (sendThumbnail != null) {
+ try {
+ sendThumbnail.requestThumbnail(token);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
+ mService.sendPendingThumbnail(null, token, null, null, true);
+ }
+ }
+
+ // Stop any activities that are scheduled to do so but have been
+ // waiting for the next one to start.
+ for (i=0; i<NS; i++) {
+ ActivityRecord r = (ActivityRecord)stops.get(i);
+ synchronized (mService) {
+ if (r.finishing) {
+ finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
+ } else {
+ stopActivityLocked(r);
+ }
+ }
+ }
+
+ // Finish any activities that are scheduled to do so but have been
+ // waiting for the next one to start.
+ for (i=0; i<NF; i++) {
+ ActivityRecord r = (ActivityRecord)finishes.get(i);
+ synchronized (mService) {
+ destroyActivityLocked(r, true);
+ }
+ }
+
+ // Report back to any thumbnail receivers.
+ for (i=0; i<NT; i++) {
+ ActivityRecord r = (ActivityRecord)thumbnails.get(i);
+ mService.sendPendingThumbnail(r, null, null, null, true);
+ }
+
+ if (booting) {
+ mService.finishBooting();
+ }
+
+ mService.trimApplications();
+ //dump();
+ //mWindowManager.dump();
+
+ if (enableScreen) {
+ mService.enableScreenAfterBoot();
+ }
+ }
+
+ /**
+ * @return Returns true if the activity is being finished, false if for
+ * some reason it is being left as-is.
+ */
+ final boolean requestFinishActivityLocked(IBinder token, int resultCode,
+ Intent resultData, String reason) {
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Finishing activity: token=" + token
+ + ", result=" + resultCode + ", data=" + resultData);
+
+ int index = indexOfTokenLocked(token);
+ if (index < 0) {
+ return false;
+ }
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+
+ // Is this the last activity left?
+ boolean lastActivity = true;
+ for (int i=mHistory.size()-1; i>=0; i--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ if (!p.finishing && p != r) {
+ lastActivity = false;
+ break;
+ }
+ }
+
+ // If this is the last activity, but it is the home activity, then
+ // just don't finish it.
+ if (lastActivity) {
+ if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
+ return false;
+ }
+ }
+
+ finishActivityLocked(r, index, resultCode, resultData, reason);
+ return true;
+ }
+
+ /**
+ * @return Returns true if this activity has been removed from the history
+ * list, or false if it is still in the list and will be removed later.
+ */
+ final boolean finishActivityLocked(ActivityRecord r, int index,
+ int resultCode, Intent resultData, String reason) {
+ if (r.finishing) {
+ Slog.w(TAG, "Duplicate finish request for " + r);
+ return false;
+ }
+
+ r.finishing = true;
+ EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName, reason);
+ r.task.numActivities--;
+ if (index < (mHistory.size()-1)) {
+ ActivityRecord next = (ActivityRecord)mHistory.get(index+1);
+ if (next.task == r.task) {
+ if (r.frontOfTask) {
+ // The next activity is now the front of the task.
+ next.frontOfTask = true;
+ }
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // If the caller asked that this activity (and all above it)
+ // be cleared when the task is reset, don't lose that information,
+ // but propagate it up to the next activity.
+ next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ }
+ }
+ }
+
+ r.pauseKeyDispatchingLocked();
+ if (mMainStack) {
+ if (mService.mFocusedActivity == r) {
+ mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+ }
+ }
+
+ // send the result
+ ActivityRecord resultTo = r.resultTo;
+ if (resultTo != null) {
+ if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
+ + " who=" + r.resultWho + " req=" + r.requestCode
+ + " res=" + resultCode + " data=" + resultData);
+ if (r.info.applicationInfo.uid > 0) {
+ mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
+ r.packageName, resultData, r.getUriPermissionsLocked());
+ }
+ resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
+ resultData);
+ r.resultTo = null;
+ }
+ else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
+
+ // Make sure this HistoryRecord is not holding on to other resources,
+ // because clients have remote IPC references to this object so we
+ // can't assume that will go away and want to avoid circular IPC refs.
+ r.results = null;
+ r.pendingResults = null;
+ r.newIntents = null;
+ r.icicle = null;
+
+ if (mService.mPendingThumbnails.size() > 0) {
+ // There are clients waiting to receive thumbnails so, in case
+ // this is an activity that someone is waiting for, add it
+ // to the pending list so we can correctly update the clients.
+ mService.mCancelledThumbnails.add(r);
+ }
+
+ if (mResumedActivity == r) {
+ boolean endTask = index <= 0
+ || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare close transition: finishing " + r);
+ mService.mWindowManager.prepareAppTransition(endTask
+ ? WindowManagerPolicy.TRANSIT_TASK_CLOSE
+ : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
+
+ // Tell window manager to prepare for this one to be removed.
+ mService.mWindowManager.setAppVisibility(r, false);
+
+ if (mPausingActivity == null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
+ startPausingLocked(false, false);
+ }
+
+ } else if (r.state != ActivityState.PAUSING) {
+ // If the activity is PAUSING, we will complete the finish once
+ // it is done pausing; else we can just directly finish it here.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
+ return finishCurrentActivityLocked(r, index,
+ FINISH_AFTER_PAUSE) == null;
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
+ }
+
+ return false;
+ }
+
+ private static final int FINISH_IMMEDIATELY = 0;
+ private static final int FINISH_AFTER_PAUSE = 1;
+ private static final int FINISH_AFTER_VISIBLE = 2;
+
+ private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
+ int mode) {
+ final int index = indexOfTokenLocked(r);
+ if (index < 0) {
+ return null;
+ }
+
+ return finishCurrentActivityLocked(r, index, mode);
+ }
+
+ private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
+ int index, int mode) {
+ // First things first: if this activity is currently visible,
+ // and the resumed activity is not yet visible, then hold off on
+ // finishing until the resumed one becomes visible.
+ if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
+ if (!mStoppingActivities.contains(r)) {
+ mStoppingActivities.add(r);
+ if (mStoppingActivities.size() > 3) {
+ // If we already have a few activities waiting to stop,
+ // then give up on things going idle and start clearing
+ // them out.
+ Message msg = Message.obtain();
+ msg.what = IDLE_NOW_MSG;
+ mHandler.sendMessage(msg);
+ }
+ }
+ r.state = ActivityState.STOPPING;
+ mService.updateOomAdjLocked();
+ return r;
+ }
+
+ // make sure the record is cleaned out of other places.
+ mStoppingActivities.remove(r);
+ mWaitingVisibleActivities.remove(r);
+ if (mResumedActivity == r) {
+ mResumedActivity = null;
+ }
+ final ActivityState prevState = r.state;
+ r.state = ActivityState.FINISHING;
+
+ if (mode == FINISH_IMMEDIATELY
+ || prevState == ActivityState.STOPPED
+ || prevState == ActivityState.INITIALIZING) {
+ // If this activity is already stopped, we can just finish
+ // it right now.
+ return destroyActivityLocked(r, true) ? null : r;
+ } else {
+ // Need to go through the full pause cycle to get this
+ // activity into the stopped state and then finish it.
+ if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
+ mFinishingActivities.add(r);
+ resumeTopActivityLocked(null);
+ }
+ return r;
+ }
+
+ /**
+ * Perform the common clean-up of an activity record. This is called both
+ * as part of destroyActivityLocked() (when destroying the client-side
+ * representation) and cleaning things up as a result of its hosting
+ * processing going away, in which case there is no remaining client-side
+ * state to destroy so only the cleanup here is needed.
+ */
+ final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) {
+ if (mResumedActivity == r) {
+ mResumedActivity = null;
+ }
+ if (mService.mFocusedActivity == r) {
+ mService.mFocusedActivity = null;
+ }
+
+ r.configDestroy = false;
+ r.frozenBeforeDestroy = false;
+
+ // Make sure this record is no longer in the pending finishes list.
+ // This could happen, for example, if we are trimming activities
+ // down to the max limit while they are still waiting to finish.
+ mFinishingActivities.remove(r);
+ mWaitingVisibleActivities.remove(r);
+
+ // Remove any pending results.
+ if (r.finishing && r.pendingResults != null) {
+ for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
+ PendingIntentRecord rec = apr.get();
+ if (rec != null) {
+ mService.cancelIntentSenderLocked(rec, false);
+ }
+ }
+ r.pendingResults = null;
+ }
+
+ if (cleanServices) {
+ cleanUpActivityServicesLocked(r);
+ }
+
+ if (mService.mPendingThumbnails.size() > 0) {
+ // There are clients waiting to receive thumbnails so, in case
+ // this is an activity that someone is waiting for, add it
+ // to the pending list so we can correctly update the clients.
+ mService.mCancelledThumbnails.add(r);
+ }
+
+ // Get rid of any pending idle timeouts.
+ mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+ }
+
+ private final void removeActivityFromHistoryLocked(ActivityRecord r) {
+ if (r.state != ActivityState.DESTROYED) {
+ mHistory.remove(r);
+ r.inHistory = false;
+ r.state = ActivityState.DESTROYED;
+ mService.mWindowManager.removeAppToken(r);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ cleanUpActivityServicesLocked(r);
+ r.removeUriPermissionsLocked();
+ }
+ }
+
+ /**
+ * Perform clean-up of service connections in an activity record.
+ */
+ final void cleanUpActivityServicesLocked(ActivityRecord r) {
+ // Throw away any services that have been bound by this activity.
+ if (r.connections != null) {
+ Iterator<ConnectionRecord> it = r.connections.iterator();
+ while (it.hasNext()) {
+ ConnectionRecord c = it.next();
+ mService.removeConnectionLocked(c, null, r);
+ }
+ r.connections = null;
+ }
+ }
+
+ /**
+ * Destroy the current CLIENT SIDE instance of an activity. This may be
+ * called both when actually finishing an activity, or when performing
+ * a configuration switch where we destroy the current client-side object
+ * but then create a new client-side object for this same HistoryRecord.
+ */
+ final boolean destroyActivityLocked(ActivityRecord r,
+ boolean removeFromApp) {
+ if (DEBUG_SWITCH) Slog.v(
+ TAG, "Removing activity: token=" + r
+ + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+ EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+
+ boolean removedFromHistory = false;
+
+ cleanUpActivityLocked(r, false);
+
+ final boolean hadApp = r.app != null;
+
+ if (hadApp) {
+ if (removeFromApp) {
+ int idx = r.app.activities.indexOf(r);
+ if (idx >= 0) {
+ r.app.activities.remove(idx);
+ }
+ if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
+ mService.mHeavyWeightProcess = null;
+ mService.mHandler.sendEmptyMessage(
+ ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
+ if (r.persistent) {
+ mService.decPersistentCountLocked(r.app);
+ }
+ if (r.app.activities.size() == 0) {
+ // No longer have activities, so update location in
+ // LRU list.
+ mService.updateLruProcessLocked(r.app, true, false);
+ }
+ }
+
+ boolean skipDestroy = false;
+
+ try {
+ if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
+ r.app.thread.scheduleDestroyActivity(r, r.finishing,
+ r.configChangeFlags);
+ } catch (Exception e) {
+ // We can just ignore exceptions here... if the process
+ // has crashed, our death notification will clean things
+ // up.
+ //Slog.w(TAG, "Exception thrown during finish", e);
+ if (r.finishing) {
+ removeActivityFromHistoryLocked(r);
+ removedFromHistory = true;
+ skipDestroy = true;
+ }
+ }
+
+ r.app = null;
+ r.nowVisible = false;
+
+ if (r.finishing && !skipDestroy) {
+ r.state = ActivityState.DESTROYING;
+ Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
+ msg.obj = r;
+ mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
+ } else {
+ r.state = ActivityState.DESTROYED;
+ }
+ } else {
+ // remove this record from the history.
+ if (r.finishing) {
+ removeActivityFromHistoryLocked(r);
+ removedFromHistory = true;
+ } else {
+ r.state = ActivityState.DESTROYED;
+ }
+ }
+
+ r.configChangeFlags = 0;
+
+ if (!mLRUActivities.remove(r) && hadApp) {
+ Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
+ }
+
+ return removedFromHistory;
+ }
+
+ final void activityDestroyed(IBinder token) {
+ synchronized (mService) {
+ mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
+
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ if (r.state == ActivityState.DESTROYING) {
+ final long origId = Binder.clearCallingIdentity();
+ removeActivityFromHistoryLocked(r);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+ }
+
+ private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) {
+ int i = list.size();
+ if (localLOGV) Slog.v(
+ TAG, "Removing app " + app + " from list " + list
+ + " with " + i + " entries");
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)list.get(i);
+ if (localLOGV) Slog.v(
+ TAG, "Record #" + i + " " + r + ": app=" + r.app);
+ if (r.app == app) {
+ if (localLOGV) Slog.v(TAG, "Removing this entry!");
+ list.remove(i);
+ }
+ }
+ }
+
+ void removeHistoryRecordsForAppLocked(ProcessRecord app) {
+ removeHistoryRecordsForAppLocked(mLRUActivities, app);
+ removeHistoryRecordsForAppLocked(mStoppingActivities, app);
+ removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
+ removeHistoryRecordsForAppLocked(mFinishingActivities, app);
+ }
+
+ final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
+
+ final int task = tr.taskId;
+ int top = mHistory.size()-1;
+
+ if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
+ // nothing to do!
+ return;
+ }
+
+ ArrayList moved = new ArrayList();
+
+ // Applying the affinities may have removed entries from the history,
+ // so get the size again.
+ top = mHistory.size()-1;
+ int pos = top;
+
+ // Shift all activities with this task up to the top
+ // of the stack, keeping them in the same internal order.
+ while (pos >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+ if (localLOGV) Slog.v(
+ TAG, "At " + pos + " ckp " + r.task + ": " + r);
+ boolean first = true;
+ if (r.task.taskId == task) {
+ if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
+ mHistory.remove(pos);
+ mHistory.add(top, r);
+ moved.add(0, r);
+ top--;
+ if (first && mMainStack) {
+ mService.addRecentTaskLocked(r.task);
+ first = false;
+ }
+ }
+ pos--;
+ }
+
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare to front transition: task=" + tr);
+ if (reason != null &&
+ (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ mNoAnimActivities.add(r);
+ }
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
+ }
+
+ mService.mWindowManager.moveAppTokensToTop(moved);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ finishTaskMoveLocked(task);
+ EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task);
+ }
+
+ private final void finishTaskMoveLocked(int task) {
+ resumeTopActivityLocked(null);
+ }
+
+ /**
+ * Worker method for rearranging history stack. Implements the function of moving all
+ * activities for a specific task (gathering them if disjoint) into a single group at the
+ * bottom of the stack.
+ *
+ * If a watcher is installed, the action is preflighted and the watcher has an opportunity
+ * to premeptively cancel the move.
+ *
+ * @param task The taskId to collect and move to the bottom.
+ * @return Returns true if the move completed, false if not.
+ */
+ final boolean moveTaskToBackLocked(int task, ActivityRecord reason) {
+ Slog.i(TAG, "moveTaskToBack: " + task);
+
+ // If we have a watcher, preflight the move before committing to it. First check
+ // for *other* available tasks, but if none are available, then try again allowing the
+ // current task to be selected.
+ if (mMainStack && mService.mController != null) {
+ ActivityRecord next = topRunningActivityLocked(null, task);
+ if (next == null) {
+ next = topRunningActivityLocked(null, 0);
+ }
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean moveOK = true;
+ try {
+ moveOK = mService.mController.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ }
+ if (!moveOK) {
+ return false;
+ }
+ }
+ }
+
+ ArrayList moved = new ArrayList();
+
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare to back transition: task=" + task);
+
+ final int N = mHistory.size();
+ int bottom = 0;
+ int pos = 0;
+
+ // Shift all activities with this task down to the bottom
+ // of the stack, keeping them in the same internal order.
+ while (pos < N) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+ if (localLOGV) Slog.v(
+ TAG, "At " + pos + " ckp " + r.task + ": " + r);
+ if (r.task.taskId == task) {
+ if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
+ mHistory.remove(pos);
+ mHistory.add(bottom, r);
+ moved.add(r);
+ bottom++;
+ }
+ pos++;
+ }
+
+ if (reason != null &&
+ (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ mNoAnimActivities.add(r);
+ }
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
+ }
+ mService.mWindowManager.moveAppTokensToBottom(moved);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ finishTaskMoveLocked(task);
+ return true;
+ }
+
+ private final void logStartActivity(int tag, ActivityRecord r,
+ TaskRecord task) {
+ EventLog.writeEvent(tag,
+ System.identityHashCode(r), task.taskId,
+ r.shortComponentName, r.intent.getAction(),
+ r.intent.getType(), r.intent.getDataString(),
+ r.intent.getFlags());
+ }
+
+ /**
+ * Make sure the given activity matches the current configuration. Returns
+ * false if the activity had to be destroyed. Returns true if the
+ * configuration is the same, or the activity will remain running as-is
+ * for whatever reason. Ensures the HistoryRecord is updated with the
+ * correct configuration and all other bookkeeping is handled.
+ */
+ final boolean ensureActivityConfigurationLocked(ActivityRecord r,
+ int globalChanges) {
+ if (mConfigWillChange) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Skipping config check (will change): " + r);
+ return true;
+ }
+
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Ensuring correct configuration: " + r);
+
+ // Short circuit: if the two configurations are the exact same
+ // object (the common case), then there is nothing to do.
+ Configuration newConfig = mService.mConfiguration;
+ if (r.configuration == newConfig) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration unchanged in " + r);
+ return true;
+ }
+
+ // We don't worry about activities that are finishing.
+ if (r.finishing) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration doesn't matter in finishing " + r);
+ r.stopFreezingScreenLocked(false);
+ return true;
+ }
+
+ // Okay we now are going to make this activity have the new config.
+ // But then we need to figure out how it needs to deal with that.
+ Configuration oldConfig = r.configuration;
+ r.configuration = newConfig;
+
+ // If the activity isn't currently running, just leave the new
+ // configuration and it will pick that up next time it starts.
+ if (r.app == null || r.app.thread == null) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration doesn't matter not running " + r);
+ r.stopFreezingScreenLocked(false);
+ return true;
+ }
+
+ // If the activity isn't persistent, there is a chance we will
+ // need to restart it.
+ if (!r.persistent) {
+
+ // Figure out what has changed between the two configurations.
+ int changes = oldConfig.diff(newConfig);
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
+ + Integer.toHexString(changes) + ", handles=0x"
+ + Integer.toHexString(r.info.configChanges)
+ + ", newConfig=" + newConfig);
+ }
+ if ((changes&(~r.info.configChanges)) != 0) {
+ // Aha, the activity isn't handling the change, so DIE DIE DIE.
+ r.configChangeFlags |= changes;
+ r.startFreezingScreenLocked(r.app, globalChanges);
+ if (r.app == null || r.app.thread == null) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is destroying non-running " + r);
+ destroyActivityLocked(r, true);
+ } else if (r.state == ActivityState.PAUSING) {
+ // A little annoying: we are waiting for this activity to
+ // finish pausing. Let's not do anything now, but just
+ // flag that it needs to be restarted when done pausing.
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is skipping already pausing " + r);
+ r.configDestroy = true;
+ return true;
+ } else if (r.state == ActivityState.RESUMED) {
+ // Try to optimize this case: the configuration is changing
+ // and we need to restart the top, resumed activity.
+ // Instead of doing the normal handshaking, just say
+ // "restart!".
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is restarting resumed " + r);
+ relaunchActivityLocked(r, r.configChangeFlags, true);
+ r.configChangeFlags = 0;
+ } else {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is restarting non-resumed " + r);
+ relaunchActivityLocked(r, r.configChangeFlags, false);
+ r.configChangeFlags = 0;
+ }
+
+ // All done... tell the caller we weren't able to keep this
+ // activity around.
+ return false;
+ }
+ }
+
+ // Default case: the activity can handle this new configuration, so
+ // hand it over. Note that we don't need to give it the new
+ // configuration, since we always send configuration changes to all
+ // process when they happen so it can just use whatever configuration
+ // it last got.
+ if (r.app != null && r.app.thread != null) {
+ try {
+ if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
+ r.app.thread.scheduleActivityConfigurationChanged(r);
+ } catch (RemoteException e) {
+ // If process died, whatever.
+ }
+ }
+ r.stopFreezingScreenLocked(false);
+
+ return true;
+ }
+
+ private final boolean relaunchActivityLocked(ActivityRecord r,
+ int changes, boolean andResume) {
+ List<ResultInfo> results = null;
+ List<Intent> newIntents = null;
+ if (andResume) {
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
+ EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
+ : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+
+ r.startFreezingScreenLocked(r.app, 0);
+
+ try {
+ if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
+ r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
+ changes, !andResume, mService.mConfiguration);
+ // Note: don't need to call pauseIfSleepingLocked() here, because
+ // the caller will only pass in 'andResume' if this activity is
+ // currently resumed, which implies we aren't sleeping.
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ if (andResume) {
+ r.results = null;
+ r.newIntents = null;
+ if (mMainStack) {
+ mService.reportResumedActivityLocked(r);
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index 3a1aad6..a769c05 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -80,9 +80,6 @@ class AppErrorDialog extends BaseErrorDialog {
DISMISS_TIMEOUT);
}
- public void onStop() {
- }
-
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
synchronized (mProc) {
diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java
index 9702f91..b2737dc 100644
--- a/services/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/java/com/android/server/am/AppNotRespondingDialog.java
@@ -40,7 +40,7 @@ class AppNotRespondingDialog extends BaseErrorDialog {
private final ProcessRecord mProc;
public AppNotRespondingDialog(ActivityManagerService service, Context context,
- ProcessRecord app, HistoryRecord activity) {
+ ProcessRecord app, ActivityRecord activity) {
super(context);
mService = service;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 33bbc13..7314e04 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -59,7 +59,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
public void shutdown() {
Slog.w("BatteryStats", "Writing battery stats before shutdown...");
synchronized (mStats) {
- mStats.writeLocked();
+ mStats.shutdownLocked();
}
}
@@ -93,45 +93,45 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
return data;
}
- public void noteStartWakelock(int uid, String name, int type) {
+ public void noteStartWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
+ mStats.noteStartWakeLocked(uid, pid, name, type);
}
}
- public void noteStopWakelock(int uid, String name, int type) {
+ public void noteStopWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
+ mStats.noteStopWakeLocked(uid, pid, name, type);
}
}
public void noteStartSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartSensor(sensor);
+ mStats.noteStartSensorLocked(uid, sensor);
}
}
public void noteStopSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopSensor(sensor);
+ mStats.noteStopSensorLocked(uid, sensor);
}
}
public void noteStartGps(int uid) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteStartGps(uid);
+ mStats.noteStartGpsLocked(uid);
}
}
public void noteStopGps(int uid) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteStopGps(uid);
+ mStats.noteStopGpsLocked(uid);
}
}
@@ -321,14 +321,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
return mStats.isOnBattery();
}
- public void setOnBattery(boolean onBattery, int level) {
+ public void setBatteryState(int status, int health, int plugType, int level,
+ int temp, int volt) {
enforceCallingPermission();
- mStats.setOnBattery(onBattery, level);
- }
-
- public void recordCurrentLevel(int level) {
- enforceCallingPermission();
- mStats.recordCurrentLevel(level);
+ mStats.setBatteryState(status, health, plugType, level, temp, volt);
}
public long getAwakeTimeBattery() {
@@ -359,7 +355,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
for (String arg : args) {
if ("--checkin".equals(arg)) {
isCheckin = true;
- break;
+ } else if ("--reset".equals(arg)) {
+ mStats.resetAllStatsLocked();
}
}
}
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index c3f0b3e..bac21b1 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -26,6 +26,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.List;
@@ -73,61 +74,65 @@ class BroadcastRecord extends Binder {
ActivityInfo curReceiver; // info about the receiver that is currently running.
void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + this);
- pw.println(prefix + intent);
+ final long now = SystemClock.uptimeMillis();
+
+ pw.print(prefix); pw.println(this);
+ pw.print(prefix); pw.println(intent);
if (sticky) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
- pw.println(prefix + "extras: " + bundle.toString());
+ pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
}
}
- pw.println(prefix + "proc=" + callerApp);
- pw.println(prefix + "caller=" + callerPackage
- + " callingPid=" + callingPid
- + " callingUid=" + callingUid);
+ pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.println(" ");
+ pw.println(callerApp != null ? callerApp.toShortString() : "null");
+ pw.print(" pid="); pw.print(callingPid);
+ pw.print(" uid="); pw.println(callingUid);
if (requiredPermission != null) {
- pw.println(prefix + "requiredPermission=" + requiredPermission);
+ pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
}
- pw.println(prefix + "dispatchTime=" + dispatchTime + " ("
- + (SystemClock.uptimeMillis()-dispatchTime) + "ms since now)");
+ pw.print(prefix); pw.print("dispatchTime=");
+ TimeUtils.formatDuration(dispatchTime, now, pw);
if (finishTime != 0) {
- pw.println(prefix + "finishTime=" + finishTime + " ("
- + (SystemClock.uptimeMillis()-finishTime) + "ms since now)");
+ pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
} else {
- pw.println(prefix + "receiverTime=" + receiverTime + " ("
- + (SystemClock.uptimeMillis()-receiverTime) + "ms since now)");
+ pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
}
+ pw.println("");
if (anrCount != 0) {
- pw.println(prefix + "anrCount=" + anrCount);
+ pw.print(prefix); pw.print("anrCount="); pw.println(anrCount);
}
if (resultTo != null || resultCode != -1 || resultData != null) {
- pw.println(prefix + "resultTo=" + resultTo
- + " resultCode=" + resultCode + " resultData=" + resultData);
+ pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
+ pw.print(" resultCode="); pw.print(resultCode);
+ pw.print(" resultData="); pw.println(resultData);
}
if (resultExtras != null) {
- pw.println(prefix + "resultExtras=" + resultExtras);
+ pw.print(prefix); pw.print("resultExtras="); pw.println(resultExtras);
}
if (resultAbort || ordered || sticky || initialSticky) {
- pw.println(prefix + "resultAbort=" + resultAbort
- + " ordered=" + ordered + " sticky=" + sticky
- + " initialSticky=" + initialSticky);
+ pw.print(prefix); pw.print("resultAbort="); pw.print(resultAbort);
+ pw.print(" ordered="); pw.print(ordered);
+ pw.print(" sticky="); pw.print(sticky);
+ pw.print(" initialSticky="); pw.println(initialSticky);
}
if (nextReceiver != 0 || receiver != null) {
- pw.println(prefix + "nextReceiver=" + nextReceiver
- + " receiver=" + receiver);
+ pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver);
+ pw.print(" receiver="); pw.println(receiver);
}
if (curFilter != null) {
- pw.println(prefix + "curFilter=" + curFilter);
+ pw.print(prefix); pw.print("curFilter="); pw.println(curFilter);
}
if (curReceiver != null) {
- pw.println(prefix + "curReceiver=" + curReceiver);
+ pw.print(prefix); pw.print("curReceiver="); pw.println(curReceiver);
}
if (curApp != null) {
- pw.println(prefix + "curApp=" + curApp);
- pw.println(prefix + "curComponent="
- + (curComponent != null ? curComponent.toShortString() : "--"));
+ pw.print(prefix); pw.print("curApp="); pw.println(curApp);
+ pw.print(prefix); pw.print("curComponent=");
+ pw.println((curComponent != null ? curComponent.toShortString() : "--"));
if (curReceiver != null && curReceiver.applicationInfo != null) {
- pw.println(prefix + "curSourceDir=" + curReceiver.applicationInfo.sourceDir);
+ pw.print(prefix); pw.print("curSourceDir=");
+ pw.println(curReceiver.applicationInfo.sourceDir);
}
}
String stateStr = " (?)";
@@ -137,13 +142,14 @@ class BroadcastRecord extends Binder {
case CALL_IN_RECEIVE: stateStr=" (CALL_IN_RECEIVE)"; break;
case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
}
- pw.println(prefix + "state=" + state + stateStr);
+ pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
final int N = receivers != null ? receivers.size() : 0;
String p2 = prefix + " ";
PrintWriterPrinter printer = new PrintWriterPrinter(pw);
for (int i=0; i<N; i++) {
Object o = receivers.get(i);
- pw.println(prefix + "Receiver #" + i + ": " + o);
+ pw.print(prefix); pw.print("Receiver #"); pw.print(i);
+ pw.print(": "); pw.println(o);
if (o instanceof BroadcastFilter)
((BroadcastFilter)o).dumpBrief(pw, p2);
else if (o instanceof ResolveInfo)
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index f613b00..22acda9 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -26,7 +26,7 @@ import java.io.PrintWriter;
*/
class ConnectionRecord {
final AppBindRecord binding; // The application/service binding.
- final HistoryRecord activity; // If non-null, the owning activity.
+ final ActivityRecord activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
final int flags; // Binding options.
final int clientLabel; // String resource labeling this client.
@@ -42,7 +42,7 @@ class ConnectionRecord {
+ " flags=0x" + Integer.toHexString(flags));
}
- ConnectionRecord(AppBindRecord _binding, HistoryRecord _activity,
+ ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity,
IServiceConnection _conn, int _flags,
int _clientLabel, PendingIntent _clientIntent) {
binding = _binding;
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index c764635..44c9742 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.app.IActivityManager.ContentProviderHolder;
+import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.os.Process;
@@ -29,6 +30,7 @@ class ContentProviderRecord extends ContentProviderHolder {
final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
final int uid;
final ApplicationInfo appInfo;
+ final ComponentName name;
int externals; // number of non-framework processes supported by this provider
ProcessRecord app; // if non-null, hosting application
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
@@ -38,6 +40,7 @@ class ContentProviderRecord extends ContentProviderHolder {
super(_info);
uid = ai.uid;
appInfo = ai;
+ name = new ComponentName(_info.packageName, _info.name);
noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
}
@@ -45,6 +48,7 @@ class ContentProviderRecord extends ContentProviderHolder {
super(cpr.info);
uid = cpr.uid;
appInfo = cpr.appInfo;
+ name = cpr.name;
noReleaseNeeded = cpr.noReleaseNeeded;
}
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 847e91b..7a85eb8 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -42,7 +42,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
final static class Key {
final int type;
final String packageName;
- final HistoryRecord activity;
+ final ActivityRecord activity;
final String who;
final int requestCode;
final Intent requestIntent;
@@ -52,7 +52,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
private static final int ODD_PRIME_NUMBER = 37;
- Key(int _t, String _p, HistoryRecord _a, String _w,
+ Key(int _t, String _p, ActivityRecord _a, String _w,
int _r, Intent _i, String _it, int _f) {
type = _t;
packageName = _p;
@@ -218,7 +218,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
}
break;
case IActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
- owner.sendActivityResultLocked(-1, key.activity,
+ key.activity.stack.sendActivityResultLocked(-1, key.activity,
key.who, key.requestCode, code, finalIntent);
break;
case IActivityManager.INTENT_SENDER_BROADCAST:
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index f49a182..6d1fbab 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.Watchdog;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -27,9 +26,9 @@ import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -40,12 +39,12 @@ import java.util.HashSet;
* Full information about a particular process that
* is currently running.
*/
-class ProcessRecord implements Watchdog.PssRequestor {
+class ProcessRecord {
final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics
final ApplicationInfo info; // all about the first app in the process
final String processName; // name of the process
// List of packages running in the process
- final HashSet<String> pkgList = new HashSet();
+ final HashSet<String> pkgList = new HashSet<String>();
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
@@ -75,6 +74,7 @@ class ProcessRecord implements Watchdog.PssRequestor {
Bundle instrumentationArguments;// as given to us
ComponentName instrumentationResultClass;// copy of instrumentationClass
BroadcastRecord curReceiver;// receiver currently running in the app
+ long lastWakeTime; // How long proc held wake lock at last check
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
boolean reportLowMemory; // Set to true when waiting to report low mem
@@ -87,9 +87,9 @@ class ProcessRecord implements Watchdog.PssRequestor {
Object adjTarget; // Debugging: target component impacting oom_adj.
// contains HistoryRecord objects
- final ArrayList activities = new ArrayList();
+ final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
// all ServiceRecord running in this process
- final HashSet services = new HashSet();
+ final HashSet<ServiceRecord> services = new HashSet<ServiceRecord>();
// services that are currently executing code (need to remain foreground).
final HashSet<ServiceRecord> executingServices
= new HashSet<ServiceRecord>();
@@ -99,7 +99,8 @@ class ProcessRecord implements Watchdog.PssRequestor {
// all IIntentReceivers that are registered from this process.
final HashSet<ReceiverList> receivers = new HashSet<ReceiverList>();
// class (String) -> ContentProviderRecord
- final HashMap pubProviders = new HashMap();
+ final HashMap<String, ContentProviderRecord> pubProviders
+ = new HashMap<String, ContentProviderRecord>();
// All ContentProviderRecord process is using
final HashMap<ContentProviderRecord, Integer> conProviders
= new HashMap<ContentProviderRecord, Integer>();
@@ -128,7 +129,8 @@ class ProcessRecord implements Watchdog.PssRequestor {
ComponentName errorReportReceiver;
void dump(PrintWriter pw, String prefix) {
- long now = SystemClock.uptimeMillis();
+ final long now = SystemClock.uptimeMillis();
+
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
@@ -158,8 +160,9 @@ class ProcessRecord implements Watchdog.PssRequestor {
pw.print(" curReceiver="); pw.println(curReceiver);
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
- pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime);
- pw.print(" lruWeight="); pw.println(lruWeight);
+ pw.print(prefix); pw.print("lastActivityTime=");
+ TimeUtils.formatDuration(lastActivityTime, now, pw);
+ pw.print(" lruWeight="); pw.print(lruWeight);
pw.print(" hidden="); pw.print(hidden);
pw.print(" empty="); pw.println(empty);
pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
@@ -178,6 +181,12 @@ class ProcessRecord implements Watchdog.PssRequestor {
pw.print(" persistentActivities="); pw.println(persistentActivities);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.println(lruSeq);
+ pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
+ pw.print(" lastRequestedGc=");
+ TimeUtils.formatDuration(lastRequestedGc, now, pw);
+ pw.print(" lastLowMemory=");
+ TimeUtils.formatDuration(lastLowMemory, now, pw);
+ pw.print(" reportLowMemory="); pw.println(reportLowMemory);
if (killedBackground) {
pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
}
@@ -249,7 +258,7 @@ class ProcessRecord implements Watchdog.PssRequestor {
public boolean isInterestingToUserLocked() {
final int size = activities.size();
for (int i = 0 ; i < size ; i++) {
- HistoryRecord r = (HistoryRecord) activities.get(i);
+ ActivityRecord r = activities.get(i);
if (r.isInterestingToUserLocked()) {
return true;
}
@@ -261,17 +270,7 @@ class ProcessRecord implements Watchdog.PssRequestor {
int i = activities.size();
while (i > 0) {
i--;
- ((HistoryRecord)activities.get(i)).stopFreezingScreenLocked(true);
- }
- }
-
- public void requestPss() {
- IApplicationThread localThread = thread;
- if (localThread != null) {
- try {
- localThread.requestPss();
- } catch (RemoteException e) {
- }
+ activities.get(i).stopFreezingScreenLocked(true);
}
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 0542497..f35a68e 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.NotificationManagerService;
import android.app.INotificationManager;
import android.app.Notification;
@@ -30,10 +31,12 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -41,6 +44,12 @@ import java.util.List;
* A running application service.
*/
class ServiceRecord extends Binder {
+ // Maximum number of delivery attempts before giving up.
+ static final int MAX_DELIVERY_COUNT = 3;
+
+ // Maximum number of times it can fail during execution before giving up.
+ static final int MAX_DONE_EXECUTING_COUNT = 6;
+
final ActivityManagerService ams;
final BatteryStatsImpl.Uid.Pkg.Serv stats;
final ComponentName name; // service component.
@@ -63,32 +72,9 @@ class ServiceRecord extends Binder {
final HashMap<Intent.FilterComparison, IntentBindRecord> bindings
= new HashMap<Intent.FilterComparison, IntentBindRecord>();
// All active bindings to the service.
- final HashMap<IBinder, ConnectionRecord> connections
- = new HashMap<IBinder, ConnectionRecord>();
+ final HashMap<IBinder, ArrayList<ConnectionRecord>> connections
+ = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
// IBinder -> ConnectionRecord of all bound clients
-
- // Maximum number of delivery attempts before giving up.
- static final int MAX_DELIVERY_COUNT = 3;
-
- // Maximum number of times it can fail during execution before giving up.
- static final int MAX_DONE_EXECUTING_COUNT = 6;
-
- static class StartItem {
- final int id;
- final Intent intent;
- long deliveredTime;
- int deliveryCount;
- int doneExecutingCount;
-
- StartItem(int _id, Intent _intent) {
- id = _id;
- intent = _intent;
- }
- }
- final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
- // start() arguments which been delivered.
- final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
- // start() arguments that haven't yet been delivered.
ProcessRecord app; // where this service is running or null.
boolean isForeground; // is service currently in foreground mode?
@@ -110,22 +96,93 @@ class ServiceRecord extends Binder {
String stringName; // caching of toString
+ static class StartItem {
+ final ServiceRecord sr;
+ final int id;
+ final Intent intent;
+ final int targetPermissionUid;
+ long deliveredTime;
+ int deliveryCount;
+ int doneExecutingCount;
+ UriPermissionOwner uriPermissions;
+
+ String stringName; // caching of toString
+
+ StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+ sr = _sr;
+ id = _id;
+ intent = _intent;
+ targetPermissionUid = _targetPermissionUid;
+ }
+
+ UriPermissionOwner getUriPermissionsLocked() {
+ if (uriPermissions == null) {
+ uriPermissions = new UriPermissionOwner(sr.ams, this);
+ }
+ return uriPermissions;
+ }
+
+ void removeUriPermissionsLocked() {
+ if (uriPermissions != null) {
+ uriPermissions.removeUriPermissionsLocked();
+ uriPermissions = null;
+ }
+ }
+
+ public String toString() {
+ if (stringName != null) {
+ return stringName;
+ }
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("ServiceRecord{")
+ .append(Integer.toHexString(System.identityHashCode(sr)))
+ .append(' ').append(sr.shortName)
+ .append(" StartItem ")
+ .append(Integer.toHexString(System.identityHashCode(this)))
+ .append(" id=").append(id).append('}');
+ return stringName = sb.toString();
+ }
+ }
+
+ final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
+ // start() arguments which been delivered.
+ final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
+ // start() arguments that haven't yet been delivered.
+
void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
final int N = list.size();
for (int i=0; i<N; i++) {
StartItem si = list.get(i);
pw.print(prefix); pw.print("#"); pw.print(i);
pw.print(" id="); pw.print(si.id);
- if (now != 0) pw.print(" dur="); pw.print(now-si.deliveredTime);
+ if (now != 0) {
+ pw.print(" dur=");
+ TimeUtils.formatDuration(si.deliveredTime, now, pw);
+ }
if (si.deliveryCount != 0) {
pw.print(" dc="); pw.print(si.deliveryCount);
}
if (si.doneExecutingCount != 0) {
pw.print(" dxc="); pw.print(si.doneExecutingCount);
}
- pw.print(" ");
+ pw.println("");
+ pw.print(prefix); pw.print(" intent=");
if (si.intent != null) pw.println(si.intent.toString());
else pw.println("null");
+ if (si.targetPermissionUid >= 0) {
+ pw.print(prefix); pw.print(" targetPermissionUid=");
+ pw.println(si.targetPermissionUid);
+ }
+ if (si.uriPermissions != null) {
+ if (si.uriPermissions.readUriPermissions != null) {
+ pw.print(prefix); pw.print(" readUriPermissions=");
+ pw.println(si.uriPermissions.readUriPermissions);
+ }
+ if (si.uriPermissions.writeUriPermissions != null) {
+ pw.print(prefix); pw.print(" writeUriPermissions=");
+ pw.println(si.uriPermissions.writeUriPermissions);
+ }
+ }
}
}
@@ -139,18 +196,26 @@ class ServiceRecord extends Binder {
pw.print(prefix); pw.print("permission="); pw.println(permission);
}
long now = SystemClock.uptimeMillis();
- pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
- if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
- pw.print(" dataDir="); pw.println(dataDir);
+ long nowReal = SystemClock.elapsedRealtime();
+ pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
+ if (!resDir.equals(baseDir)) pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
if (isForeground || foregroundId != 0) {
pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
pw.print(" foregroundId="); pw.print(foregroundId);
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
}
- pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now);
- pw.print(" executingStart="); pw.print(executingStart-now);
- pw.print(" restartTime="); pw.println(restartTime);
+ pw.print(prefix); pw.print("createTime=");
+ TimeUtils.formatDuration(createTime, nowReal, pw);
+ pw.print(" lastActivity=");
+ TimeUtils.formatDuration(lastActivity, now, pw);
+ pw.println("");
+ pw.print(prefix); pw.print(" executingStart=");
+ TimeUtils.formatDuration(executingStart, now, pw);
+ pw.print(" restartTime=");
+ TimeUtils.formatDuration(restartTime, now, pw);
+ pw.println("");
if (startRequested || lastStartId != 0) {
pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
pw.print(" stopIfKilled="); pw.print(stopIfKilled);
@@ -161,13 +226,15 @@ class ServiceRecord extends Binder {
|| restartDelay != 0 || nextRestartTime != 0) {
pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
pw.print(" restartCount="); pw.print(restartCount);
- pw.print(" restartDelay="); pw.print(restartDelay-now);
- pw.print(" nextRestartTime="); pw.print(nextRestartTime-now);
+ pw.print(" restartDelay=");
+ TimeUtils.formatDuration(restartDelay, now, pw);
+ pw.print(" nextRestartTime=");
+ TimeUtils.formatDuration(nextRestartTime, now, pw);
pw.print(" crashCount="); pw.println(crashCount);
}
if (deliveredStarts.size() > 0) {
pw.print(prefix); pw.println("Delivered Starts:");
- dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
+ dumpStartList(pw, prefix, deliveredStarts, now);
}
if (pendingStarts.size() > 0) {
pw.print(prefix); pw.println("Pending Starts:");
@@ -186,10 +253,12 @@ class ServiceRecord extends Binder {
}
if (connections.size() > 0) {
pw.print(prefix); pw.println("All Connections:");
- Iterator<ConnectionRecord> it = connections.values().iterator();
+ Iterator<ArrayList<ConnectionRecord>> it = connections.values().iterator();
while (it.hasNext()) {
- ConnectionRecord c = it.next();
- pw.print(prefix); pw.print(" "); pw.println(c);
+ ArrayList<ConnectionRecord> c = it.next();
+ for (int i=0; i<c.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(c.get(i));
+ }
}
}
}
@@ -212,7 +281,8 @@ class ServiceRecord extends Binder {
dataDir = sInfo.applicationInfo.dataDir;
exported = sInfo.exported;
this.restarter = restarter;
- createTime = lastActivity = SystemClock.uptimeMillis();
+ createTime = SystemClock.elapsedRealtime();
+ lastActivity = SystemClock.uptimeMillis();
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
@@ -252,6 +322,8 @@ class ServiceRecord extends Binder {
}
public void postNotification() {
+ final int appUid = appInfo.uid;
+ final int appPid = app.pid;
if (foregroundId != 0 && foregroundNoti != null) {
// Do asynchronous communication with notification manager to
// avoid deadlocks.
@@ -260,14 +332,15 @@ class ServiceRecord extends Binder {
final Notification localForegroundNoti = foregroundNoti;
ams.mHandler.post(new Runnable() {
public void run() {
- INotificationManager inm = NotificationManager.getService();
- if (inm == null) {
+ NotificationManagerService nm =
+ (NotificationManagerService) NotificationManager.getService();
+ if (nm == null) {
return;
}
try {
int[] outId = new int[1];
- inm.enqueueNotification(localPackageName, localForegroundId,
- localForegroundNoti, outId);
+ nm.enqueueNotificationInternal(localPackageName, appUid, appPid,
+ null, localForegroundId, localForegroundNoti, outId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for service", e);
@@ -275,7 +348,6 @@ class ServiceRecord extends Binder {
// get to be foreground.
ams.setServiceForeground(name, ServiceRecord.this,
localForegroundId, null, true);
- } catch (RemoteException e) {
}
}
});
@@ -306,6 +378,13 @@ class ServiceRecord extends Binder {
}
}
+ public void clearDeliveredStartsLocked() {
+ for (int i=deliveredStarts.size()-1; i>=0; i--) {
+ deliveredStarts.get(i).removeUriPermissionsLocked();
+ }
+ deliveredStarts.clear();
+ }
+
public String toString() {
if (stringName != null) {
return stringName;
diff --git a/services/java/com/android/server/am/StrictModeViolationDialog.java b/services/java/com/android/server/am/StrictModeViolationDialog.java
new file mode 100644
index 0000000..fe76d18
--- /dev/null
+++ b/services/java/com/android/server/am/StrictModeViolationDialog.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2006 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 static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Slog;
+
+class StrictModeViolationDialog extends BaseErrorDialog {
+ private final static String TAG = "StrictModeViolationDialog";
+
+ private final AppErrorResult mResult;
+ private final ProcessRecord mProc;
+
+ // Event 'what' codes
+ static final int ACTION_OK = 0;
+ static final int ACTION_OK_AND_REPORT = 1;
+
+ // 1-minute timeout, then we automatically dismiss the violation
+ // dialog
+ static final long DISMISS_TIMEOUT = 1000 * 60 * 1;
+
+ public StrictModeViolationDialog(Context context, AppErrorResult result, ProcessRecord app) {
+ super(context);
+
+ Resources res = context.getResources();
+
+ mProc = app;
+ mResult = result;
+ CharSequence name;
+ if ((app.pkgList.size() == 1) &&
+ (name=context.getPackageManager().getApplicationLabel(app.info)) != null) {
+ setMessage(res.getString(
+ com.android.internal.R.string.smv_application,
+ name.toString(), app.info.processName));
+ } else {
+ name = app.processName;
+ setMessage(res.getString(
+ com.android.internal.R.string.smv_process,
+ name.toString()));
+ }
+
+ setCancelable(false);
+
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ res.getText(com.android.internal.R.string.dlg_ok),
+ mHandler.obtainMessage(ACTION_OK));
+
+ if (app.errorReportReceiver != null) {
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ res.getText(com.android.internal.R.string.report),
+ mHandler.obtainMessage(ACTION_OK_AND_REPORT));
+ }
+
+ setTitle(res.getText(com.android.internal.R.string.aerr_title));
+ getWindow().addFlags(FLAG_SYSTEM_ERROR);
+ getWindow().setTitle("Strict Mode Violation: " + app.info.processName);
+
+ // After the timeout, pretend the user clicked the quit button
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(ACTION_OK),
+ DISMISS_TIMEOUT);
+ }
+
+ private final Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ synchronized (mProc) {
+ if (mProc != null && mProc.crashDialog == StrictModeViolationDialog.this) {
+ mProc.crashDialog = null;
+ }
+ }
+ mResult.set(msg.what);
+
+ // If this is a timeout we won't be automatically closed, so go
+ // ahead and explicitly dismiss ourselves just in case.
+ dismiss();
+ }
+ };
+}
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index ffa8a2a..c95546e 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -22,13 +22,21 @@ import android.net.Uri;
import java.io.PrintWriter;
import java.util.HashSet;
+/**
+ * Description of a permission granted to an app to access a particular URI.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert
+ * /src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ */
class UriPermission {
final int uid;
final Uri uri;
int modeFlags = 0;
int globalModeFlags = 0;
- final HashSet<HistoryRecord> readActivities = new HashSet<HistoryRecord>();
- final HashSet<HistoryRecord> writeActivities = new HashSet<HistoryRecord>();
+ final HashSet<UriPermissionOwner> readOwners = new HashSet<UriPermissionOwner>();
+ final HashSet<UriPermissionOwner> writeOwners = new HashSet<UriPermissionOwner>();
String stringName;
@@ -41,27 +49,21 @@ class UriPermission {
if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- if (readActivities.size() > 0) {
- for (HistoryRecord r : readActivities) {
- r.readUriPermissions.remove(this);
- if (r.readUriPermissions.size() == 0) {
- r.readUriPermissions = null;
- }
+ if (readOwners.size() > 0) {
+ for (UriPermissionOwner r : readOwners) {
+ r.removeReadPermission(this);
}
- readActivities.clear();
+ readOwners.clear();
}
}
if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- if (readActivities.size() > 0) {
- for (HistoryRecord r : readActivities) {
- r.writeUriPermissions.remove(this);
- if (r.writeUriPermissions.size() == 0) {
- r.writeUriPermissions = null;
- }
+ if (readOwners.size() > 0) {
+ for (UriPermissionOwner r : writeOwners) {
+ r.removeWritePermission(this);
}
- readActivities.clear();
+ readOwners.clear();
}
}
}
@@ -85,11 +87,17 @@ class UriPermission {
pw.print(" uid="); pw.print(uid);
pw.print(" globalModeFlags=0x");
pw.println(Integer.toHexString(globalModeFlags));
- if (readActivities.size() != 0) {
- pw.print(prefix); pw.print("readActivities="); pw.println(readActivities);
+ if (readOwners.size() != 0) {
+ pw.print(prefix); pw.println("readOwners:");
+ for (UriPermissionOwner owner : readOwners) {
+ pw.print(prefix); pw.print(" * "); pw.println(owner);
+ }
}
- if (writeActivities.size() != 0) {
- pw.print(prefix); pw.print("writeActivities="); pw.println(writeActivities);
+ if (writeOwners.size() != 0) {
+ pw.print(prefix); pw.println("writeOwners:");
+ for (UriPermissionOwner owner : writeOwners) {
+ pw.print(prefix); pw.print(" * "); pw.println(owner);
+ }
}
}
}
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
new file mode 100644
index 0000000..99c82e6
--- /dev/null
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+class UriPermissionOwner {
+ final ActivityManagerService service;
+ final Object owner;
+
+ Binder externalToken;
+
+ HashSet<UriPermission> readUriPermissions; // special access to reading uris.
+ HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+
+ class ExternalToken extends Binder {
+ UriPermissionOwner getOwner() {
+ return UriPermissionOwner.this;
+ }
+ }
+
+ UriPermissionOwner(ActivityManagerService _service, Object _owner) {
+ service = _service;
+ owner = _owner;
+ }
+
+ Binder getExternalTokenLocked() {
+ if (externalToken != null) {
+ externalToken = new ExternalToken();
+ }
+ return externalToken;
+ }
+
+ static UriPermissionOwner fromExternalToken(IBinder token) {
+ if (token instanceof ExternalToken) {
+ return ((ExternalToken)token).getOwner();
+ }
+ return null;
+ }
+
+ void removeUriPermissionsLocked() {
+ removeUriPermissionsLocked(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+
+ void removeUriPermissionsLocked(int mode) {
+ if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
+ && readUriPermissions != null) {
+ for (UriPermission perm : readUriPermissions) {
+ perm.readOwners.remove(this);
+ if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ }
+ readUriPermissions = null;
+ }
+ if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+ && writeUriPermissions != null) {
+ for (UriPermission perm : writeUriPermissions) {
+ perm.writeOwners.remove(this);
+ if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ }
+ writeUriPermissions = null;
+ }
+ }
+
+ void removeUriPermissionLocked(Uri uri, int mode) {
+ if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
+ && readUriPermissions != null) {
+ Iterator<UriPermission> it = readUriPermissions.iterator();
+ while (it.hasNext()) {
+ UriPermission perm = it.next();
+ if (uri.equals(perm.uri)) {
+ perm.readOwners.remove(this);
+ if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ it.remove();
+ }
+ }
+ if (readUriPermissions.size() == 0) {
+ readUriPermissions = null;
+ }
+ }
+ if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+ && writeUriPermissions != null) {
+ Iterator<UriPermission> it = writeUriPermissions.iterator();
+ while (it.hasNext()) {
+ UriPermission perm = it.next();
+ if (uri.equals(perm.uri)) {
+ perm.writeOwners.remove(this);
+ if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ it.remove();
+ }
+ }
+ if (writeUriPermissions.size() == 0) {
+ writeUriPermissions = null;
+ }
+ }
+ }
+
+ public void addReadPermission(UriPermission perm) {
+ if (readUriPermissions == null) {
+ readUriPermissions = new HashSet<UriPermission>();
+ }
+ readUriPermissions.add(perm);
+ }
+
+ public void addWritePermission(UriPermission perm) {
+ if (writeUriPermissions == null) {
+ writeUriPermissions = new HashSet<UriPermission>();
+ }
+ writeUriPermissions.add(perm);
+ }
+
+ public void removeReadPermission(UriPermission perm) {
+ readUriPermissions.remove(perm);
+ if (readUriPermissions.size() == 0) {
+ readUriPermissions = null;
+ }
+ }
+
+ public void removeWritePermission(UriPermission perm) {
+ writeUriPermissions.remove(perm);
+ if (writeUriPermissions.size() == 0) {
+ writeUriPermissions = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return owner.toString();
+ }
+}
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 1b9e1c7..3f15d0a 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -44,6 +44,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
/**
* This service collects the statistics associated with usage
@@ -88,11 +91,13 @@ public final class UsageStatsService extends IUsageStats.Stub {
private boolean mIsResumed;
private File mFile;
private String mFileLeaf;
- //private File mBackupFile;
- private long mLastWriteElapsedTime;
private File mDir;
- private Calendar mCal;
- private int mLastWriteDay;
+
+ private Calendar mCal; // guarded by itself
+
+ private final AtomicInteger mLastWriteDay = new AtomicInteger(-1);
+ private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
+ private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
static class TimeStats {
int count;
@@ -241,31 +246,33 @@ public final class UsageStatsService extends IUsageStats.Stub {
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
mFile = new File(mDir, mFileLeaf);
readStatsFromFile();
- mLastWriteElapsedTime = SystemClock.elapsedRealtime();
+ mLastWriteElapsedTime.set(SystemClock.elapsedRealtime());
// mCal was set by getCurrentDateStr(), want to use that same time.
- mLastWriteDay = mCal.get(Calendar.DAY_OF_YEAR);
+ mLastWriteDay.set(mCal.get(Calendar.DAY_OF_YEAR));
}
/*
* Utility method to convert date into string.
*/
private String getCurrentDateStr(String prefix) {
- mCal.setTimeInMillis(System.currentTimeMillis());
StringBuilder sb = new StringBuilder();
- if (prefix != null) {
- sb.append(prefix);
- }
- sb.append(mCal.get(Calendar.YEAR));
- int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
- if (mm < 10) {
- sb.append("0");
- }
- sb.append(mm);
- int dd = mCal.get(Calendar.DAY_OF_MONTH);
- if (dd < 10) {
- sb.append("0");
+ synchronized (mCal) {
+ mCal.setTimeInMillis(System.currentTimeMillis());
+ if (prefix != null) {
+ sb.append(prefix);
+ }
+ sb.append(mCal.get(Calendar.YEAR));
+ int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
+ if (mm < 10) {
+ sb.append("0");
+ }
+ sb.append(mm);
+ int dd = mCal.get(Calendar.DAY_OF_MONTH);
+ if (dd < 10) {
+ sb.append("0");
+ }
+ sb.append(dd);
}
- sb.append(dd);
return sb.toString();
}
@@ -360,23 +367,56 @@ public final class UsageStatsService extends IUsageStats.Stub {
file.delete();
}
}
-
- private void writeStatsToFile(boolean force) {
- synchronized (mFileLock) {
+
+ /**
+ * Conditionally start up a disk write if it's been awhile, or the
+ * day has rolled over.
+ *
+ * This is called indirectly from user-facing actions (when
+ * 'force' is false) so it tries to be quick, without writing to
+ * disk directly or acquiring heavy locks.
+ *
+ * @params force do an unconditional, synchronous stats flush
+ * to disk on the current thread.
+ */
+ private void writeStatsToFile(final boolean force) {
+ int curDay;
+ synchronized (mCal) {
mCal.setTimeInMillis(System.currentTimeMillis());
- final int curDay = mCal.get(Calendar.DAY_OF_YEAR);
- // Determine if the day changed... note that this will be wrong
- // if the year has changed but we are in the same day of year...
- // we can probably live with this.
- final boolean dayChanged = curDay != mLastWriteDay;
- long currElapsedTime = SystemClock.elapsedRealtime();
- if (!force) {
- if (((currElapsedTime-mLastWriteElapsedTime) < FILE_WRITE_INTERVAL) &&
- (!dayChanged)) {
- // wait till the next update
- return;
- }
+ curDay = mCal.get(Calendar.DAY_OF_YEAR);
+ }
+ final boolean dayChanged = curDay != mLastWriteDay.get();
+
+ // Determine if the day changed... note that this will be wrong
+ // if the year has changed but we are in the same day of year...
+ // we can probably live with this.
+ final long currElapsedTime = SystemClock.elapsedRealtime();
+
+ // Fast common path, without taking the often-contentious
+ // mFileLock.
+ if (!force) {
+ if (!dayChanged &&
+ (currElapsedTime - mLastWriteElapsedTime.get()) < FILE_WRITE_INTERVAL) {
+ // wait till the next update
+ return;
}
+ if (mUnforcedDiskWriteRunning.compareAndSet(false, true)) {
+ new Thread("UsageStatsService_DiskWriter") {
+ public void run() {
+ try {
+ Slog.d(TAG, "Disk writer thread starting.");
+ writeStatsToFile(true);
+ } finally {
+ mUnforcedDiskWriteRunning.set(false);
+ Slog.d(TAG, "Disk writer thread ending.");
+ }
+ }
+ }.start();
+ }
+ return;
+ }
+
+ synchronized (mFileLock) {
// Get the most recent file
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
// Copy current file to back up
@@ -395,10 +435,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
try {
// Write mStats to file
- writeStatsFLOCK();
- mLastWriteElapsedTime = currElapsedTime;
+ writeStatsFLOCK(mFile);
+ mLastWriteElapsedTime.set(currElapsedTime);
if (dayChanged) {
- mLastWriteDay = curDay;
+ mLastWriteDay.set(curDay);
// clear stats
synchronized (mStats) {
mStats.clear();
@@ -418,10 +458,11 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
}
}
+ Slog.d(TAG, "Dumped usage stats.");
}
- private void writeStatsFLOCK() throws IOException {
- FileOutputStream stream = new FileOutputStream(mFile);
+ private void writeStatsFLOCK(File file) throws IOException {
+ FileOutputStream stream = new FileOutputStream(file);
try {
Parcel out = Parcel.obtain();
writeStatsToParcelFLOCK(out);
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index b29f875..eb0a8a9 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -26,13 +26,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.Usb;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkInfo;
import android.net.NetworkUtils;
-import android.os.BatteryManager;
import android.os.Binder;
import android.os.Environment;
import android.os.HandlerThread;
@@ -135,7 +135,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mStateReceiver = new StateReceiver();
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Usb.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(mStateReceiver, filter);
@@ -424,10 +424,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private class StateReceiver extends BroadcastReceiver {
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
- if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- mUsbConnected = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
- == BatteryManager.BATTERY_PLUGGED_USB);
- Tethering.this.updateUsbStatus();
+ if (action.equals(Usb.ACTION_USB_STATE)) {
+ mUsbConnected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
+ updateUsbStatus();
} else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
mUsbMassStorageOff = false;
updateUsbStatus();
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
new file mode 100644
index 0000000..3c05da2
--- /dev/null
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Address;
+import android.location.GeocoderParams;
+import android.location.IGeocodeProvider;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * A class for proxying IGeocodeProvider implementations.
+ *
+ * {@hide}
+ */
+public class GeocoderProxy {
+
+ private static final String TAG = "GeocoderProxy";
+
+ private final Context mContext;
+ private final Intent mIntent;
+ private final Connection mServiceConnection = new Connection();
+ private IGeocodeProvider mProvider;
+
+ public GeocoderProxy(Context context, String serviceName) {
+ mContext = context;
+ mIntent = new Intent(serviceName);
+ mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ private class Connection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "onServiceConnected " + className);
+ synchronized (this) {
+ mProvider = IGeocodeProvider.Stub.asInterface(service);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "onServiceDisconnected " + className);
+ synchronized (this) {
+ mProvider = null;
+ }
+ }
+ }
+
+ public String getFromLocation(double latitude, double longitude, int maxResults,
+ GeocoderParams params, List<Address> addrs) {
+ IGeocodeProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.getFromLocation(latitude, longitude, maxResults,
+ params, addrs);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocation failed", e);
+ }
+ }
+ return "Service not Available";
+ }
+
+ public String getFromLocationName(String locationName,
+ double lowerLeftLatitude, double lowerLeftLongitude,
+ double upperRightLatitude, double upperRightLongitude, int maxResults,
+ GeocoderParams params, List<Address> addrs) {
+ IGeocodeProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.getFromLocationName(locationName, lowerLeftLatitude,
+ lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+ maxResults, params, addrs);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocationName failed", e);
+ }
+ }
+ return "Service not Available";
+ }
+}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
new file mode 100755
index 0000000..c1165c7
--- /dev/null
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -0,0 +1,1473 @@
+/*
+ * Copyright (C) 2008 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.location;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.location.Criteria;
+import android.location.IGpsStatusListener;
+import android.location.IGpsStatusProvider;
+import android.location.ILocationManager;
+import android.location.INetInitiatedListener;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.SntpClient;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.Phone;
+import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.StringBufferInputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Properties;
+import java.util.Map.Entry;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A GPS implementation of LocationProvider used by LocationManager.
+ *
+ * {@hide}
+ */
+public class GpsLocationProvider implements LocationProviderInterface {
+
+ private static final String TAG = "GpsLocationProvider";
+
+ private static final boolean DEBUG = false;
+ private static final boolean VERBOSE = false;
+
+ // these need to match GpsPositionMode enum in gps.h
+ private static final int GPS_POSITION_MODE_STANDALONE = 0;
+ private static final int GPS_POSITION_MODE_MS_BASED = 1;
+ private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
+
+ // these need to match GpsPositionRecurrence enum in gps.h
+ private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
+ private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
+
+ // these need to match GpsStatusValue defines in gps.h
+ private static final int GPS_STATUS_NONE = 0;
+ private static final int GPS_STATUS_SESSION_BEGIN = 1;
+ private static final int GPS_STATUS_SESSION_END = 2;
+ private static final int GPS_STATUS_ENGINE_ON = 3;
+ private static final int GPS_STATUS_ENGINE_OFF = 4;
+
+ // these need to match GpsApgsStatusValue defines in gps.h
+ /** AGPS status event values. */
+ private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
+ private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
+ private static final int GPS_AGPS_DATA_CONNECTED = 3;
+ private static final int GPS_AGPS_DATA_CONN_DONE = 4;
+ private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
+
+ // these need to match GpsLocationFlags enum in gps.h
+ private static final int LOCATION_INVALID = 0;
+ private static final int LOCATION_HAS_LAT_LONG = 1;
+ private static final int LOCATION_HAS_ALTITUDE = 2;
+ private static final int LOCATION_HAS_SPEED = 4;
+ private static final int LOCATION_HAS_BEARING = 8;
+ private static final int LOCATION_HAS_ACCURACY = 16;
+
+// IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
+ private static final int GPS_DELETE_EPHEMERIS = 0x0001;
+ private static final int GPS_DELETE_ALMANAC = 0x0002;
+ private static final int GPS_DELETE_POSITION = 0x0004;
+ private static final int GPS_DELETE_TIME = 0x0008;
+ private static final int GPS_DELETE_IONO = 0x0010;
+ private static final int GPS_DELETE_UTC = 0x0020;
+ private static final int GPS_DELETE_HEALTH = 0x0040;
+ private static final int GPS_DELETE_SVDIR = 0x0080;
+ private static final int GPS_DELETE_SVSTEER = 0x0100;
+ private static final int GPS_DELETE_SADATA = 0x0200;
+ private static final int GPS_DELETE_RTI = 0x0400;
+ private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
+ private static final int GPS_DELETE_ALL = 0xFFFF;
+
+ // The GPS_CAPABILITY_* flags must match the values in gps.h
+ private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
+ private static final int GPS_CAPABILITY_MSB = 0x0000002;
+ private static final int GPS_CAPABILITY_MSA = 0x0000004;
+ private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
+
+
+ // these need to match AGpsType enum in gps.h
+ private static final int AGPS_TYPE_SUPL = 1;
+ private static final int AGPS_TYPE_C2K = 2;
+
+ // for mAGpsDataConnectionState
+ private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
+ private static final int AGPS_DATA_CONNECTION_OPENING = 1;
+ private static final int AGPS_DATA_CONNECTION_OPEN = 2;
+
+ // Handler messages
+ private static final int CHECK_LOCATION = 1;
+ private static final int ENABLE = 2;
+ private static final int ENABLE_TRACKING = 3;
+ private static final int UPDATE_NETWORK_STATE = 4;
+ private static final int INJECT_NTP_TIME = 5;
+ private static final int DOWNLOAD_XTRA_DATA = 6;
+ private static final int UPDATE_LOCATION = 7;
+ private static final int ADD_LISTENER = 8;
+ private static final int REMOVE_LISTENER = 9;
+ private static final int REQUEST_SINGLE_SHOT = 10;
+
+ private static final String PROPERTIES_FILE = "/etc/gps.conf";
+
+ private int mLocationFlags = LOCATION_INVALID;
+
+ // current status
+ private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
+
+ // time for last status update
+ private long mStatusUpdateTime = SystemClock.elapsedRealtime();
+
+ // turn off GPS fix icon if we haven't received a fix in 10 seconds
+ private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
+
+ // stop trying if we do not receive a fix within 60 seconds
+ private static final int NO_FIX_TIMEOUT = 60 * 1000;
+
+ // true if we are enabled
+ private volatile boolean mEnabled;
+
+ // true if we have network connectivity
+ private boolean mNetworkAvailable;
+
+ // flags to trigger NTP or XTRA data download when network becomes available
+ // initialized to true so we do NTP and XTRA when the network comes up after booting
+ private boolean mInjectNtpTimePending = true;
+ private boolean mDownloadXtraDataPending = false;
+
+ // true if GPS is navigating
+ private boolean mNavigating;
+
+ // true if GPS engine is on
+ private boolean mEngineOn;
+
+ // requested frequency of fixes, in milliseconds
+ private int mFixInterval = 1000;
+
+ // true if we started navigation
+ private boolean mStarted;
+
+ // true if single shot request is in progress
+ private boolean mSingleShot;
+
+ // capabilities of the GPS engine
+ private int mEngineCapabilities;
+
+ // true if XTRA is supported
+ private boolean mSupportsXtra;
+
+ // for calculating time to first fix
+ private long mFixRequestTime = 0;
+ // time to first fix for most recent session
+ private int mTTFF = 0;
+ // time we received our last fix
+ private long mLastFixTime;
+
+ private int mPositionMode;
+
+ // properties loaded from PROPERTIES_FILE
+ private Properties mProperties;
+ private String mNtpServer;
+ private String mSuplServerHost;
+ private int mSuplServerPort;
+ private String mC2KServerHost;
+ private int mC2KServerPort;
+
+ private final Context mContext;
+ private final ILocationManager mLocationManager;
+ private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
+ private Bundle mLocationExtras = new Bundle();
+ private ArrayList<Listener> mListeners = new ArrayList<Listener>();
+
+ // GpsLocationProvider's handler thread
+ private final Thread mThread;
+ // Handler for processing events in mThread.
+ private Handler mHandler;
+ // Used to signal when our main thread has initialized everything
+ private final CountDownLatch mInitializedLatch = new CountDownLatch(1);
+
+ private String mAGpsApn;
+ private int mAGpsDataConnectionState;
+ private final ConnectivityManager mConnMgr;
+ private final GpsNetInitiatedHandler mNIHandler;
+
+ // Wakelocks
+ private final static String WAKELOCK_KEY = "GpsLocationProvider";
+ private final PowerManager.WakeLock mWakeLock;
+ // bitfield of pending messages to our Handler
+ // used only for messages that cannot have multiple instances queued
+ private int mPendingMessageBits;
+ // separate counter for ADD_LISTENER and REMOVE_LISTENER messages,
+ // which might have multiple instances queued
+ private int mPendingListenerMessages;
+
+ // Alarms
+ private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
+ private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
+ private final AlarmManager mAlarmManager;
+ private final PendingIntent mWakeupIntent;
+ private final PendingIntent mTimeoutIntent;
+
+ private final IBatteryStats mBatteryStats;
+ private final SparseIntArray mClientUids = new SparseIntArray();
+
+ // how often to request NTP time, in milliseconds
+ // current setting 4 hours
+ private static final long NTP_INTERVAL = 4*60*60*1000;
+ // how long to wait if we have a network error in NTP or XTRA downloading
+ // current setting - 5 minutes
+ private static final long RETRY_INTERVAL = 5*60*1000;
+
+ // to avoid injecting bad NTP time, we reject any time fixes that differ from system time
+ // by more than 5 minutes.
+ private static final long MAX_NTP_SYSTEM_TIME_OFFSET = 5*60*1000;
+
+ private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
+ public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
+ if (listener == null) {
+ throw new NullPointerException("listener is null in addGpsStatusListener");
+ }
+
+ synchronized(mListeners) {
+ IBinder binder = listener.asBinder();
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener test = mListeners.get(i);
+ if (binder.equals(test.mListener.asBinder())) {
+ // listener already added
+ return;
+ }
+ }
+
+ Listener l = new Listener(listener);
+ binder.linkToDeath(l, 0);
+ mListeners.add(l);
+ }
+ }
+
+ public void removeGpsStatusListener(IGpsStatusListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("listener is null in addGpsStatusListener");
+ }
+
+ synchronized(mListeners) {
+ IBinder binder = listener.asBinder();
+ Listener l = null;
+ int size = mListeners.size();
+ for (int i = 0; i < size && l == null; i++) {
+ Listener test = mListeners.get(i);
+ if (binder.equals(test.mListener.asBinder())) {
+ l = test;
+ }
+ }
+
+ if (l != null) {
+ mListeners.remove(l);
+ binder.unlinkToDeath(l, 0);
+ }
+ }
+ }
+ };
+
+ public IGpsStatusProvider getGpsStatusProvider() {
+ return mGpsStatusProvider;
+ }
+
+ private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
+ @Override public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(ALARM_WAKEUP)) {
+ if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
+ startNavigating(false);
+ } else if (action.equals(ALARM_TIMEOUT)) {
+ if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
+ hibernate();
+ }
+ }
+ };
+
+ public static boolean isSupported() {
+ return native_is_supported();
+ }
+
+ public GpsLocationProvider(Context context, ILocationManager locationManager) {
+ mContext = context;
+ mLocationManager = locationManager;
+ mNIHandler = new GpsNetInitiatedHandler(context);
+
+ mLocation.setExtras(mLocationExtras);
+
+ // Create a wake lock
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+ mWakeLock.setReferenceCounted(false);
+
+ mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
+ mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
+
+ mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ // Battery statistics service to be notified when GPS turns on or off
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+ mProperties = new Properties();
+ try {
+ File file = new File(PROPERTIES_FILE);
+ FileInputStream stream = new FileInputStream(file);
+ mProperties.load(stream);
+ stream.close();
+ mNtpServer = mProperties.getProperty("NTP_SERVER", null);
+
+ mSuplServerHost = mProperties.getProperty("SUPL_HOST");
+ String portString = mProperties.getProperty("SUPL_PORT");
+ if (mSuplServerHost != null && portString != null) {
+ try {
+ mSuplServerPort = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
+ }
+ }
+
+ mC2KServerHost = mProperties.getProperty("C2K_HOST");
+ portString = mProperties.getProperty("C2K_PORT");
+ if (mC2KServerHost != null && portString != null) {
+ try {
+ mC2KServerPort = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse C2K_PORT: " + portString);
+ }
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
+ }
+
+ // wait until we are fully initialized before returning
+ mThread = new GpsLocationProviderThread();
+ mThread.start();
+ while (true) {
+ try {
+ mInitializedLatch.await();
+ break;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private void initialize() {
+ // register our receiver on our thread rather than the main thread
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ALARM_WAKEUP);
+ intentFilter.addAction(ALARM_TIMEOUT);
+ mContext.registerReceiver(mBroadcastReciever, intentFilter);
+ }
+
+ /**
+ * Returns the name of this provider.
+ */
+ public String getName() {
+ return LocationManager.GPS_PROVIDER;
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * data network (e.g., the Internet), false otherwise.
+ */
+ public boolean requiresNetwork() {
+ return true;
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ sendMessage(UPDATE_NETWORK_STATE, state, info);
+ }
+
+ private void handleUpdateNetworkState(int state, NetworkInfo info) {
+ mNetworkAvailable = (state == LocationProvider.AVAILABLE);
+
+ if (DEBUG) {
+ Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
+ + " info: " + info);
+ }
+
+ if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
+ && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
+ String apnName = info.getExtraInfo();
+ if (mNetworkAvailable && apnName != null && apnName.length() > 0) {
+ mAGpsApn = apnName;
+ if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open");
+ native_agps_data_conn_open(apnName);
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
+ } else {
+ if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed");
+ mAGpsApn = null;
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ native_agps_data_conn_failed();
+ }
+ }
+
+ if (mNetworkAvailable) {
+ if (mInjectNtpTimePending) {
+ sendMessage(INJECT_NTP_TIME, 0, null);
+ }
+ if (mDownloadXtraDataPending) {
+ sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
+ }
+ }
+ }
+
+ private void handleInjectNtpTime() {
+ if (!mNetworkAvailable) {
+ // try again when network is up
+ mInjectNtpTimePending = true;
+ return;
+ }
+ mInjectNtpTimePending = false;
+
+ SntpClient client = new SntpClient();
+ long delay;
+
+ if (client.requestTime(mNtpServer, 10000)) {
+ long time = client.getNtpTime();
+ long timeReference = client.getNtpTimeReference();
+ int certainty = (int)(client.getRoundTripTime()/2);
+ long now = System.currentTimeMillis();
+
+ Log.d(TAG, "NTP server returned: "
+ + time + " (" + new Date(time)
+ + ") reference: " + timeReference
+ + " certainty: " + certainty
+ + " system time offset: " + (time - now));
+
+ native_inject_time(time, timeReference, certainty);
+ delay = NTP_INTERVAL;
+ } else {
+ if (DEBUG) Log.d(TAG, "requestTime failed");
+ delay = RETRY_INTERVAL;
+ }
+
+ // send delayed message for next NTP injection
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.removeMessages(INJECT_NTP_TIME);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+ }
+
+ private void handleDownloadXtraData() {
+ if (!mNetworkAvailable) {
+ // try again when network is up
+ mDownloadXtraDataPending = true;
+ return;
+ }
+ mDownloadXtraDataPending = false;
+
+
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
+ byte[] data = xtraDownloader.downloadXtraData();
+ if (data != null) {
+ if (DEBUG) {
+ Log.d(TAG, "calling native_inject_xtra_data");
+ }
+ native_inject_xtra_data(data, data.length);
+ } else {
+ // try again later
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
+ }
+ }
+
+ /**
+ * This is called to inform us when another location provider returns a location.
+ * Someday we might use this for network location injection to aid the GPS
+ */
+ public void updateLocation(Location location) {
+ sendMessage(UPDATE_LOCATION, 0, location);
+ }
+
+ private void handleUpdateLocation(Location location) {
+ if (location.hasAccuracy()) {
+ native_inject_location(location.getLatitude(), location.getLongitude(),
+ location.getAccuracy());
+ }
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * satellite-based positioning system (e.g., GPS), false
+ * otherwise.
+ */
+ public boolean requiresSatellite() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider requires access to an appropriate
+ * cellular network (e.g., to make use of cell tower IDs), false
+ * otherwise.
+ */
+ public boolean requiresCell() {
+ return false;
+ }
+
+ /**
+ * Returns true if the use of this provider may result in a
+ * monetary charge to the user, false if use is free. It is up to
+ * each provider to give accurate information.
+ */
+ public boolean hasMonetaryCost() {
+ return false;
+ }
+
+ /**
+ * Returns true if the provider is able to provide altitude
+ * information, false otherwise. A provider that reports altitude
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsAltitude() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider is able to provide speed
+ * information, false otherwise. A provider that reports speed
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsSpeed() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider is able to provide bearing
+ * information, false otherwise. A provider that reports bearing
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsBearing() {
+ return true;
+ }
+
+ /**
+ * Returns the power requirement for this provider.
+ *
+ * @return the power requirement for this provider, as one of the
+ * constants Criteria.POWER_REQUIREMENT_*.
+ */
+ public int getPowerRequirement() {
+ return Criteria.POWER_HIGH;
+ }
+
+ /**
+ * Returns true if this provider meets the given criteria,
+ * false otherwise.
+ */
+ public boolean meetsCriteria(Criteria criteria) {
+ return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
+ }
+
+ /**
+ * Returns the horizontal accuracy of this provider
+ *
+ * @return the accuracy of location from this provider, as one
+ * of the constants Criteria.ACCURACY_*.
+ */
+ public int getAccuracy() {
+ return Criteria.ACCURACY_FINE;
+ }
+
+ /**
+ * Enables this provider. When enabled, calls to getStatus()
+ * must be handled. Hardware may be started up
+ * when the provider is enabled.
+ */
+ public void enable() {
+ synchronized (mHandler) {
+ sendMessage(ENABLE, 1, null);
+ }
+ }
+
+ private void handleEnable() {
+ if (DEBUG) Log.d(TAG, "handleEnable");
+ if (mEnabled) return;
+ mEnabled = native_init();
+
+ if (mEnabled) {
+ mSupportsXtra = native_supports_xtra();
+ if (mSuplServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+ }
+ if (mC2KServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
+ }
+ } else {
+ Log.w(TAG, "Failed to enable location provider");
+ }
+ }
+
+ /**
+ * Disables this provider. When disabled, calls to getStatus()
+ * need not be handled. Hardware may be shut
+ * down while the provider is disabled.
+ */
+ public void disable() {
+ synchronized (mHandler) {
+ sendMessage(ENABLE, 0, null);
+ }
+ }
+
+ private void handleDisable() {
+ if (DEBUG) Log.d(TAG, "handleDisable");
+ if (!mEnabled) return;
+
+ mEnabled = false;
+ stopNavigating();
+
+ // do this before releasing wakelock
+ native_cleanup();
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public int getStatus(Bundle extras) {
+ if (extras != null) {
+ extras.putInt("satellites", mSvCount);
+ }
+ return mStatus;
+ }
+
+ private void updateStatus(int status, int svCount) {
+ if (status != mStatus || svCount != mSvCount) {
+ mStatus = status;
+ mSvCount = svCount;
+ mLocationExtras.putInt("satellites", svCount);
+ mStatusUpdateTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return mStatusUpdateTime;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ // FIXME - should set a flag here to avoid race conditions with single shot request
+ synchronized (mHandler) {
+ sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
+ }
+ }
+
+ private void handleEnableLocationTracking(boolean enable) {
+ if (enable) {
+ mTTFF = 0;
+ mLastFixTime = 0;
+ startNavigating(false);
+ } else {
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
+ stopNavigating();
+ }
+ }
+
+ public boolean requestSingleShotFix() {
+ if (mStarted) {
+ // cannot do single shot if already navigating
+ return false;
+ }
+ synchronized (mHandler) {
+ mHandler.removeMessages(REQUEST_SINGLE_SHOT);
+ Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT);
+ mHandler.sendMessage(m);
+ }
+ return true;
+ }
+
+ private void handleRequestSingleShot() {
+ mTTFF = 0;
+ mLastFixTime = 0;
+ startNavigating(true);
+ }
+
+ public void setMinTime(long minTime) {
+ if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
+
+ if (minTime >= 0) {
+ mFixInterval = (int)minTime;
+
+ if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
+ mFixInterval, 0, 0)) {
+ Log.e(TAG, "set_position_mode failed in setMinTime()");
+ }
+ }
+ }
+ }
+
+ public String getInternalState() {
+ return native_get_internal_state();
+ }
+
+ private final class Listener implements IBinder.DeathRecipient {
+ final IGpsStatusListener mListener;
+
+ int mSensors = 0;
+
+ Listener(IGpsStatusListener listener) {
+ mListener = listener;
+ }
+
+ public void binderDied() {
+ if (DEBUG) Log.d(TAG, "GPS status listener died");
+
+ synchronized(mListeners) {
+ mListeners.remove(this);
+ }
+ if (mListener != null) {
+ mListener.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+ }
+
+ public void addListener(int uid) {
+ synchronized (mWakeLock) {
+ mPendingListenerMessages++;
+ mWakeLock.acquire();
+ Message m = Message.obtain(mHandler, ADD_LISTENER);
+ m.arg1 = uid;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleAddListener(int uid) {
+ synchronized(mListeners) {
+ if (mClientUids.indexOfKey(uid) >= 0) {
+ // Shouldn't be here -- already have this uid.
+ Log.w(TAG, "Duplicate add listener for uid " + uid);
+ return;
+ }
+ mClientUids.put(uid, 0);
+ if (mNavigating) {
+ try {
+ mBatteryStats.noteStartGps(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in addListener");
+ }
+ }
+ }
+ }
+
+ public void removeListener(int uid) {
+ synchronized (mWakeLock) {
+ mPendingListenerMessages++;
+ mWakeLock.acquire();
+ Message m = Message.obtain(mHandler, REMOVE_LISTENER);
+ m.arg1 = uid;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleRemoveListener(int uid) {
+ synchronized(mListeners) {
+ if (mClientUids.indexOfKey(uid) < 0) {
+ // Shouldn't be here -- don't have this uid.
+ Log.w(TAG, "Unneeded remove listener for uid " + uid);
+ return;
+ }
+ mClientUids.delete(uid);
+ if (mNavigating) {
+ try {
+ mBatteryStats.noteStopGps(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in removeListener");
+ }
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+
+ long identity = Binder.clearCallingIdentity();
+ boolean result = false;
+
+ if ("delete_aiding_data".equals(command)) {
+ result = deleteAidingData(extras);
+ } else if ("force_time_injection".equals(command)) {
+ sendMessage(INJECT_NTP_TIME, 0, null);
+ result = true;
+ } else if ("force_xtra_injection".equals(command)) {
+ if (mSupportsXtra) {
+ xtraDownloadRequest();
+ result = true;
+ }
+ } else {
+ Log.w(TAG, "sendExtraCommand: unknown command " + command);
+ }
+
+ Binder.restoreCallingIdentity(identity);
+ return result;
+ }
+
+ private boolean deleteAidingData(Bundle extras) {
+ int flags;
+
+ if (extras == null) {
+ flags = GPS_DELETE_ALL;
+ } else {
+ flags = 0;
+ if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
+ if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
+ if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
+ if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
+ if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
+ if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
+ if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
+ if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
+ if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
+ if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
+ if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
+ if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
+ if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
+ }
+
+ if (flags != 0) {
+ native_delete_aiding_data(flags);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void startNavigating(boolean singleShot) {
+ if (!mStarted) {
+ if (DEBUG) Log.d(TAG, "startNavigating");
+ mStarted = true;
+ mSingleShot = singleShot;
+ mPositionMode = GPS_POSITION_MODE_STANDALONE;
+
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
+ if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
+ mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
+ } else if (hasCapability(GPS_CAPABILITY_MSB)) {
+ mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ }
+ }
+
+ int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
+ if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
+ interval, 0, 0)) {
+ mStarted = false;
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
+ if (!native_start()) {
+ mStarted = false;
+ Log.e(TAG, "native_start failed in startNavigating()");
+ return;
+ }
+
+ // reset SV count to zero
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
+ mFixRequestTime = System.currentTimeMillis();
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
+ // and our fix interval is not short
+ if (mFixInterval >= NO_FIX_TIMEOUT) {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
+ }
+ }
+ }
+ }
+
+ private void stopNavigating() {
+ if (DEBUG) Log.d(TAG, "stopNavigating");
+ if (mStarted) {
+ mStarted = false;
+ mSingleShot = false;
+ native_stop();
+ mTTFF = 0;
+ mLastFixTime = 0;
+ mLocationFlags = LOCATION_INVALID;
+
+ // reset SV count to zero
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
+ }
+ }
+
+ private void hibernate() {
+ // stop GPS until our next fix interval arrives
+ stopNavigating();
+ mAlarmManager.cancel(mTimeoutIntent);
+ mAlarmManager.cancel(mWakeupIntent);
+ long now = SystemClock.elapsedRealtime();
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent);
+ }
+
+ private boolean hasCapability(int capability) {
+ return ((mEngineCapabilities & capability) != 0);
+ }
+
+ /**
+ * called from native code to update our position.
+ */
+ private void reportLocation(int flags, double latitude, double longitude, double altitude,
+ float speed, float bearing, float accuracy, long timestamp) {
+ if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
+ " timestamp: " + timestamp);
+
+ synchronized (mLocation) {
+ mLocationFlags = flags;
+ if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mLocation.setLatitude(latitude);
+ mLocation.setLongitude(longitude);
+ mLocation.setTime(timestamp);
+ }
+ if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+ mLocation.setAltitude(altitude);
+ } else {
+ mLocation.removeAltitude();
+ }
+ if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+ mLocation.setSpeed(speed);
+ } else {
+ mLocation.removeSpeed();
+ }
+ if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+ mLocation.setBearing(bearing);
+ } else {
+ mLocation.removeBearing();
+ }
+ if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+ mLocation.setAccuracy(accuracy);
+ } else {
+ mLocation.removeAccuracy();
+ }
+
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+
+ mLastFixTime = System.currentTimeMillis();
+ // report time to first fix
+ if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mTTFF = (int)(mLastFixTime - mFixRequestTime);
+ if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF);
+
+ // notify status listeners
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onFirstFix(mTTFF);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in stopNavigating");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+
+ if (mSingleShot) {
+ stopNavigating();
+ }
+ if (mStarted && mStatus != LocationProvider.AVAILABLE) {
+ // we want to time out if we do not receive a fix
+ // within the time out and we are requesting infrequent fixes
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
+
+ // send an intent to notify that the GPS is receiving fixes.
+ Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
+ mContext.sendBroadcast(intent);
+ updateStatus(LocationProvider.AVAILABLE, mSvCount);
+ }
+
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted && mFixInterval > 1000) {
+ if (DEBUG) Log.d(TAG, "got fix, hibernating");
+ hibernate();
+ }
+ }
+
+ /**
+ * called from native code to update our status
+ */
+ private void reportStatus(int status) {
+ if (VERBOSE) Log.v(TAG, "reportStatus status: " + status);
+
+ synchronized(mListeners) {
+ boolean wasNavigating = mNavigating;
+
+ switch (status) {
+ case GPS_STATUS_SESSION_BEGIN:
+ mNavigating = true;
+ mEngineOn = true;
+ break;
+ case GPS_STATUS_SESSION_END:
+ mNavigating = false;
+ break;
+ case GPS_STATUS_ENGINE_ON:
+ mEngineOn = true;
+ break;
+ case GPS_STATUS_ENGINE_OFF:
+ mEngineOn = false;
+ mNavigating = false;
+ break;
+ }
+
+ if (wasNavigating != mNavigating) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ if (mNavigating) {
+ listener.mListener.onGpsStarted();
+ } else {
+ listener.mListener.onGpsStopped();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportStatus");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+
+ try {
+ // update battery stats
+ for (int i=mClientUids.size() - 1; i >= 0; i--) {
+ int uid = mClientUids.keyAt(i);
+ if (mNavigating) {
+ mBatteryStats.noteStartGps(uid);
+ } else {
+ mBatteryStats.noteStopGps(uid);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportStatus");
+ }
+
+ // send an intent to notify that the GPS has been enabled or disabled.
+ Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
+ mContext.sendBroadcast(intent);
+ }
+ }
+ }
+
+ /**
+ * called from native code to update SV info
+ */
+ private void reportSvStatus() {
+
+ int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
+
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
+ mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
+ mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportSvInfo");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "SV count: " + svCount +
+ " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
+ " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
+ for (int i = 0; i < svCount; i++) {
+ Log.v(TAG, "sv: " + mSvs[i] +
+ " snr: " + (float)mSnrs[i]/10 +
+ " elev: " + mSvElevations[i] +
+ " azimuth: " + mSvAzimuths[i] +
+ ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") +
+ ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " A") +
+ ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
+ }
+ }
+
+ // return number of sets used in fix instead of total
+ updateStatus(mStatus, Integer.bitCount(mSvMasks[USED_FOR_FIX_MASK]));
+
+ if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
+ System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
+ // send an intent to notify that the GPS is no longer receiving fixes.
+ Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
+ mContext.sendBroadcast(intent);
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
+ }
+ }
+
+ /**
+ * called from native code to update AGPS status
+ */
+ private void reportAGpsStatus(int type, int status) {
+ switch (status) {
+ case GPS_REQUEST_AGPS_DATA_CONN:
+ int result = mConnMgr.startUsingNetworkFeature(
+ ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+ if (result == Phone.APN_ALREADY_ACTIVE) {
+ if (mAGpsApn != null) {
+ native_agps_data_conn_open(mAGpsApn);
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
+ } else {
+ Log.e(TAG, "mAGpsApn not set when receiving Phone.APN_ALREADY_ACTIVE");
+ native_agps_data_conn_failed();
+ }
+ } else if (result == Phone.APN_REQUEST_STARTED) {
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+ } else {
+ native_agps_data_conn_failed();
+ }
+ break;
+ case GPS_RELEASE_AGPS_DATA_CONN:
+ if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
+ mConnMgr.stopUsingNetworkFeature(
+ ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+ native_agps_data_conn_closed();
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ }
+ break;
+ case GPS_AGPS_DATA_CONNECTED:
+ // Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
+ break;
+ case GPS_AGPS_DATA_CONN_DONE:
+ // Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
+ break;
+ case GPS_AGPS_DATA_CONN_FAILED:
+ // Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
+ break;
+ }
+ }
+
+ /**
+ * called from native code to report NMEA data received
+ */
+ private void reportNmea(long timestamp) {
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ if (size > 0) {
+ // don't bother creating the String if we have no listeners
+ int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
+ String nmea = new String(mNmeaBuffer, 0, length);
+
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onNmeaReceived(timestamp, nmea);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportNmea");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * called from native code to inform us what the GPS engine capabilities are
+ */
+ private void setEngineCapabilities(int capabilities) {
+ mEngineCapabilities = capabilities;
+ }
+
+ /**
+ * called from native code to request XTRA data
+ */
+ private void xtraDownloadRequest() {
+ if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
+ sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
+ }
+
+ //=============================================================
+ // NI Client support
+ //=============================================================
+ private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
+ // Sends a response for an NI reqeust to HAL.
+ public boolean sendNiResponse(int notificationId, int userResponse)
+ {
+ // TODO Add Permission check
+
+ StringBuilder extrasBuf = new StringBuilder();
+
+ if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
+ ", response: " + userResponse);
+
+ native_send_ni_response(notificationId, userResponse);
+
+ return true;
+ }
+ };
+
+ public INetInitiatedListener getNetInitiatedListener() {
+ return mNetInitiatedListener;
+ }
+
+ // Called by JNI function to report an NI request.
+ @SuppressWarnings("deprecation")
+ public void reportNiNotification(
+ int notificationId,
+ int niType,
+ int notifyFlags,
+ int timeout,
+ int defaultResponse,
+ String requestorId,
+ String text,
+ int requestorIdEncoding,
+ int textEncoding,
+ String extras // Encoded extra data
+ )
+ {
+ Log.i(TAG, "reportNiNotification: entered");
+ Log.i(TAG, "notificationId: " + notificationId +
+ ", niType: " + niType +
+ ", notifyFlags: " + notifyFlags +
+ ", timeout: " + timeout +
+ ", defaultResponse: " + defaultResponse);
+
+ Log.i(TAG, "requestorId: " + requestorId +
+ ", text: " + text +
+ ", requestorIdEncoding: " + requestorIdEncoding +
+ ", textEncoding: " + textEncoding);
+
+ GpsNiNotification notification = new GpsNiNotification();
+
+ notification.notificationId = notificationId;
+ notification.niType = niType;
+ notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
+ notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
+ notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
+ notification.timeout = timeout;
+ notification.defaultResponse = defaultResponse;
+ notification.requestorId = requestorId;
+ notification.text = text;
+ notification.requestorIdEncoding = requestorIdEncoding;
+ notification.textEncoding = textEncoding;
+
+ // Process extras, assuming the format is
+ // one of more lines of "key = value"
+ Bundle bundle = new Bundle();
+
+ if (extras == null) extras = "";
+ Properties extraProp = new Properties();
+
+ try {
+ extraProp.load(new StringBufferInputStream(extras));
+ }
+ catch (IOException e)
+ {
+ Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
+ }
+
+ for (Entry<Object, Object> ent : extraProp.entrySet())
+ {
+ bundle.putString((String) ent.getKey(), (String) ent.getValue());
+ }
+
+ notification.extras = bundle;
+
+ mNIHandler.handleNiNotification(notification);
+ }
+
+ private void sendMessage(int message, int arg, Object obj) {
+ // hold a wake lock while messages are pending
+ synchronized (mWakeLock) {
+ mPendingMessageBits |= (1 << message);
+ mWakeLock.acquire();
+ mHandler.removeMessages(message);
+ Message m = Message.obtain(mHandler, message);
+ m.arg1 = arg;
+ m.obj = obj;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private final class ProviderHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg)
+ {
+ int message = msg.what;
+ switch (message) {
+ case ENABLE:
+ if (msg.arg1 == 1) {
+ handleEnable();
+ } else {
+ handleDisable();
+ }
+ break;
+ case ENABLE_TRACKING:
+ handleEnableLocationTracking(msg.arg1 == 1);
+ break;
+ case REQUEST_SINGLE_SHOT:
+ handleRequestSingleShot();
+ break;
+ case UPDATE_NETWORK_STATE:
+ handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
+ break;
+ case INJECT_NTP_TIME:
+ handleInjectNtpTime();
+ break;
+ case DOWNLOAD_XTRA_DATA:
+ if (mSupportsXtra) {
+ handleDownloadXtraData();
+ }
+ break;
+ case UPDATE_LOCATION:
+ handleUpdateLocation((Location)msg.obj);
+ break;
+ case ADD_LISTENER:
+ handleAddListener(msg.arg1);
+ break;
+ case REMOVE_LISTENER:
+ handleRemoveListener(msg.arg1);
+ break;
+ }
+ // release wake lock if no messages are pending
+ synchronized (mWakeLock) {
+ mPendingMessageBits &= ~(1 << message);
+ if (message == ADD_LISTENER || message == REMOVE_LISTENER) {
+ mPendingListenerMessages--;
+ }
+ if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) {
+ mWakeLock.release();
+ }
+ }
+ }
+ };
+
+ private final class GpsLocationProviderThread extends Thread {
+
+ public GpsLocationProviderThread() {
+ super("GpsLocationProvider");
+ }
+
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ initialize();
+ Looper.prepare();
+ mHandler = new ProviderHandler();
+ // signal when we are initialized and ready to go
+ mInitializedLatch.countDown();
+ Looper.loop();
+ }
+ }
+
+ // for GPS SV statistics
+ private static final int MAX_SVS = 32;
+ private static final int EPHEMERIS_MASK = 0;
+ private static final int ALMANAC_MASK = 1;
+ private static final int USED_FOR_FIX_MASK = 2;
+
+ // preallocated arrays, to avoid memory allocation in reportStatus()
+ private int mSvs[] = new int[MAX_SVS];
+ private float mSnrs[] = new float[MAX_SVS];
+ private float mSvElevations[] = new float[MAX_SVS];
+ private float mSvAzimuths[] = new float[MAX_SVS];
+ private int mSvMasks[] = new int[3];
+ private int mSvCount;
+ // preallocated to avoid memory allocation in reportNmea()
+ private byte[] mNmeaBuffer = new byte[120];
+
+ static { class_init_native(); }
+ private static native void class_init_native();
+ private static native boolean native_is_supported();
+
+ private native boolean native_init();
+ private native void native_cleanup();
+ private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
+ int preferred_accuracy, int preferred_time);
+ private native boolean native_start();
+ private native boolean native_stop();
+ private native void native_delete_aiding_data(int flags);
+ // returns number of SVs
+ // mask[0] is ephemeris mask and mask[1] is almanac mask
+ private native int native_read_sv_status(int[] svs, float[] snrs,
+ float[] elevations, float[] azimuths, int[] masks);
+ private native int native_read_nmea(byte[] buffer, int bufferSize);
+ private native void native_inject_location(double latitude, double longitude, float accuracy);
+
+ // XTRA Support
+ private native void native_inject_time(long time, long timeReference, int uncertainty);
+ private native boolean native_supports_xtra();
+ private native void native_inject_xtra_data(byte[] data, int length);
+
+ // DEBUG Support
+ private native String native_get_internal_state();
+
+ // AGPS Support
+ private native void native_agps_data_conn_open(String apn);
+ private native void native_agps_data_conn_closed();
+ private native void native_agps_data_conn_failed();
+ private native void native_set_agps_server(int type, String hostname, int port);
+
+ // Network-initiated (NI) Support
+ private native void native_send_ni_response(int notificationId, int userResponse);
+}
diff --git a/services/java/com/android/server/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
new file mode 100644
index 0000000..bc96980
--- /dev/null
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008 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.location;
+
+import android.content.Context;
+import android.net.Proxy;
+import android.net.http.AndroidHttpClient;
+import android.util.Config;
+import android.util.Log;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.params.ConnRouteParams;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Random;
+
+/**
+ * A class for downloading GPS XTRA data.
+ *
+ * {@hide}
+ */
+public class GpsXtraDownloader {
+
+ private static final String TAG = "GpsXtraDownloader";
+
+ private Context mContext;
+ private String[] mXtraServers;
+ // to load balance our server requests
+ private int mNextServerIndex;
+
+ GpsXtraDownloader(Context context, Properties properties) {
+ mContext = context;
+
+ // read XTRA servers from the Properties object
+ int count = 0;
+ String server1 = properties.getProperty("XTRA_SERVER_1");
+ String server2 = properties.getProperty("XTRA_SERVER_2");
+ String server3 = properties.getProperty("XTRA_SERVER_3");
+ if (server1 != null) count++;
+ if (server2 != null) count++;
+ if (server3 != null) count++;
+
+ if (count == 0) {
+ Log.e(TAG, "No XTRA servers were specified in the GPS configuration");
+ return;
+ } else {
+ mXtraServers = new String[count];
+ count = 0;
+ if (server1 != null) mXtraServers[count++] = server1;
+ if (server2 != null) mXtraServers[count++] = server2;
+ if (server3 != null) mXtraServers[count++] = server3;
+
+ // randomize first server
+ Random random = new Random();
+ mNextServerIndex = random.nextInt(count);
+ }
+ }
+
+ byte[] downloadXtraData() {
+ String proxyHost = Proxy.getHost(mContext);
+ int proxyPort = Proxy.getPort(mContext);
+ boolean useProxy = (proxyHost != null && proxyPort != -1);
+ byte[] result = null;
+ int startIndex = mNextServerIndex;
+
+ if (mXtraServers == null) {
+ return null;
+ }
+
+ // load balance our requests among the available servers
+ while (result == null) {
+ result = doDownload(mXtraServers[mNextServerIndex], useProxy, proxyHost, proxyPort);
+
+ // increment mNextServerIndex and wrap around if necessary
+ mNextServerIndex++;
+ if (mNextServerIndex == mXtraServers.length) {
+ mNextServerIndex = 0;
+ }
+ // break if we have tried all the servers
+ if (mNextServerIndex == startIndex) break;
+ }
+
+ return result;
+ }
+
+ protected static byte[] doDownload(String url, boolean isProxySet,
+ String proxyHost, int proxyPort) {
+ if (Config.LOGD) Log.d(TAG, "Downloading XTRA data from " + url);
+
+ AndroidHttpClient client = null;
+ try {
+ client = AndroidHttpClient.newInstance("Android");
+ HttpUriRequest req = new HttpGet(url);
+
+ if (isProxySet) {
+ HttpHost proxy = new HttpHost(proxyHost, proxyPort);
+ ConnRouteParams.setDefaultProxy(req.getParams(), proxy);
+ }
+
+ req.addHeader(
+ "Accept",
+ "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
+
+ req.addHeader(
+ "x-wap-profile",
+ "http://www.openmobilealliance.org/tech/profiles/UAPROF/ccppschema-20021212#");
+
+ HttpResponse response = client.execute(req);
+ StatusLine status = response.getStatusLine();
+ if (status.getStatusCode() != 200) { // HTTP 200 is success.
+ if (Config.LOGD) Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
+ return null;
+ }
+
+ HttpEntity entity = response.getEntity();
+ byte[] body = null;
+ if (entity != null) {
+ try {
+ if (entity.getContentLength() > 0) {
+ body = new byte[(int) entity.getContentLength()];
+ DataInputStream dis = new DataInputStream(entity.getContent());
+ try {
+ dis.readFully(body);
+ } finally {
+ try {
+ dis.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Unexpected IOException.", e);
+ }
+ }
+ }
+ } finally {
+ if (entity != null) {
+ entity.consumeContent();
+ }
+ }
+ }
+ return body;
+ } catch (Exception e) {
+ if (Config.LOGD) Log.d(TAG, "error " + e);
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
new file mode 100644
index 0000000..084ab81
--- /dev/null
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.location.Criteria;
+import android.location.Location;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * {@hide}
+ */
+public interface LocationProviderInterface {
+ String getName();
+ boolean requiresNetwork();
+ boolean requiresSatellite();
+ boolean requiresCell();
+ boolean hasMonetaryCost();
+ boolean supportsAltitude();
+ boolean supportsSpeed();
+ boolean supportsBearing();
+ int getPowerRequirement();
+ boolean meetsCriteria(Criteria criteria);
+ int getAccuracy();
+ boolean isEnabled();
+ void enable();
+ void disable();
+ int getStatus(Bundle extras);
+ long getStatusUpdateTime();
+ void enableLocationTracking(boolean enable);
+ /* returns false if single shot is not supported */
+ boolean requestSingleShotFix();
+ String getInternalState();
+ void setMinTime(long minTime);
+ void updateNetworkState(int state, NetworkInfo info);
+ void updateLocation(Location location);
+ boolean sendExtraCommand(String command, Bundle extras);
+ void addListener(int uid);
+ void removeListener(int uid);
+}
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
new file mode 100644
index 0000000..24d7737
--- /dev/null
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2009 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.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Criteria;
+import android.location.ILocationProvider;
+import android.location.Location;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.internal.location.DummyLocationProvider;
+
+/**
+ * A class for proxying location providers implemented as services.
+ *
+ * {@hide}
+ */
+public class LocationProviderProxy implements LocationProviderInterface {
+
+ private static final String TAG = "LocationProviderProxy";
+
+ private final Context mContext;
+ private final String mName;
+ private ILocationProvider mProvider;
+ private Handler mHandler;
+ private final Connection mServiceConnection = new Connection();
+
+ // cached values set by the location manager
+ private boolean mLocationTracking = false;
+ private boolean mEnabled = false;
+ private long mMinTime = -1;
+ private int mNetworkState;
+ private NetworkInfo mNetworkInfo;
+
+ // for caching requiresNetwork, requiresSatellite, etc.
+ private DummyLocationProvider mCachedAttributes;
+
+ // constructor for proxying location providers implemented in a separate service
+ public LocationProviderProxy(Context context, String name, String serviceName,
+ Handler handler) {
+ mContext = context;
+ mName = name;
+ mHandler = handler;
+ mContext.bindService(new Intent(serviceName), mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ private class Connection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className);
+ synchronized (this) {
+ mProvider = ILocationProvider.Stub.asInterface(service);
+ if (mProvider != null) {
+ mHandler.post(mServiceConnectedTask);
+ }
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className);
+ synchronized (this) {
+ mProvider = null;
+ }
+ }
+ }
+
+ private Runnable mServiceConnectedTask = new Runnable() {
+ public void run() {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ if (provider == null) {
+ return;
+ }
+ }
+
+ if (mCachedAttributes == null) {
+ try {
+ mCachedAttributes = new DummyLocationProvider(mName, null);
+ mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
+ mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
+ mCachedAttributes.setRequiresCell(provider.requiresCell());
+ mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
+ mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
+ mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
+ mCachedAttributes.setSupportsBearing(provider.supportsBearing());
+ mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
+ mCachedAttributes.setAccuracy(provider.getAccuracy());
+ } catch (RemoteException e) {
+ mCachedAttributes = null;
+ }
+ }
+
+ // resend previous values from the location manager if the service has restarted
+ try {
+ if (mEnabled) {
+ provider.enable();
+ }
+ if (mLocationTracking) {
+ provider.enableLocationTracking(true);
+ }
+ if (mMinTime >= 0) {
+ provider.setMinTime(mMinTime);
+ }
+ if (mNetworkInfo != null) {
+ provider.updateNetworkState(mNetworkState, mNetworkInfo);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ };
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean requiresNetwork() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.requiresNetwork();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean requiresSatellite() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.requiresSatellite();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean requiresCell() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.requiresCell();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean hasMonetaryCost() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.hasMonetaryCost();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsAltitude() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.supportsAltitude();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsSpeed() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.supportsSpeed();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsBearing() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.supportsBearing();
+ } else {
+ return false;
+ }
+ }
+
+ public int getPowerRequirement() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.getPowerRequirement();
+ } else {
+ return -1;
+ }
+ }
+
+ public boolean meetsCriteria(Criteria criteria) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.meetsCriteria(criteria);
+ } catch (RemoteException e) {
+ }
+ }
+ // default implementation if we lost connection to the provider
+ if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
+ (criteria.getAccuracy() < getAccuracy())) {
+ return false;
+ }
+ int criteriaPower = criteria.getPowerRequirement();
+ if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
+ (criteriaPower < getPowerRequirement())) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !supportsAltitude()) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !supportsSpeed()) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !supportsBearing()) {
+ return false;
+ }
+ return true;
+ }
+
+ public int getAccuracy() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.getAccuracy();
+ } else {
+ return -1;
+ }
+ }
+
+ public void enable() {
+ mEnabled = true;
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.enable();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void disable() {
+ mEnabled = false;
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.disable();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public int getStatus(Bundle extras) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.getStatus(extras);
+ } catch (RemoteException e) {
+ }
+ }
+ return 0;
+ }
+
+ public long getStatusUpdateTime() {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.getStatusUpdateTime();
+ } catch (RemoteException e) {
+ }
+ }
+ return 0;
+ }
+
+ public String getInternalState() {
+ try {
+ return mProvider.getInternalState();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getInternalState failed", e);
+ return null;
+ }
+ }
+
+ public boolean isLocationTracking() {
+ return mLocationTracking;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ mLocationTracking = enable;
+ if (!enable) {
+ mMinTime = -1;
+ }
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.enableLocationTracking(enable);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
+ public long getMinTime() {
+ return mMinTime;
+ }
+
+ public void setMinTime(long minTime) {
+ mMinTime = minTime;
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.setMinTime(minTime);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ mNetworkState = state;
+ mNetworkInfo = info;
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.updateNetworkState(state, info);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void updateLocation(Location location) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.updateLocation(location);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.sendExtraCommand(command, extras);
+ } catch (RemoteException e) {
+ }
+ }
+ return false;
+ }
+
+ public void addListener(int uid) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.addListener(uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void removeListener(int uid) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.removeListener(uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
new file mode 100644
index 0000000..01b34b7
--- /dev/null
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2009 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.location;
+
+import android.location.Criteria;
+import android.location.ILocationManager;
+import android.location.Location;
+import android.location.LocationProvider;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+
+import java.io.PrintWriter;
+
+/**
+ * A mock location provider used by LocationManagerService to implement test providers.
+ *
+ * {@hide}
+ */
+public class MockProvider implements LocationProviderInterface {
+ private final String mName;
+ private final ILocationManager mLocationManager;
+ private final boolean mRequiresNetwork;
+ private final boolean mRequiresSatellite;
+ private final boolean mRequiresCell;
+ private final boolean mHasMonetaryCost;
+ private final boolean mSupportsAltitude;
+ private final boolean mSupportsSpeed;
+ private final boolean mSupportsBearing;
+ private final int mPowerRequirement;
+ private final int mAccuracy;
+ private final Location mLocation;
+ private int mStatus;
+ private long mStatusUpdateTime;
+ private final Bundle mExtras = new Bundle();
+ private boolean mHasLocation;
+ private boolean mHasStatus;
+ private boolean mEnabled;
+
+ private static final String TAG = "MockProvider";
+
+ public MockProvider(String name, ILocationManager locationManager,
+ boolean requiresNetwork, boolean requiresSatellite,
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ mName = name;
+ mLocationManager = locationManager;
+ mRequiresNetwork = requiresNetwork;
+ mRequiresSatellite = requiresSatellite;
+ mRequiresCell = requiresCell;
+ mHasMonetaryCost = hasMonetaryCost;
+ mSupportsAltitude = supportsAltitude;
+ mSupportsBearing = supportsBearing;
+ mSupportsSpeed = supportsSpeed;
+ mPowerRequirement = powerRequirement;
+ mAccuracy = accuracy;
+ mLocation = new Location(name);
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void disable() {
+ mEnabled = false;
+ }
+
+ public void enable() {
+ mEnabled = true;
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public int getStatus(Bundle extras) {
+ if (mHasStatus) {
+ extras.clear();
+ extras.putAll(mExtras);
+ return mStatus;
+ } else {
+ return LocationProvider.AVAILABLE;
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return mStatusUpdateTime;
+ }
+
+ public int getAccuracy() {
+ return mAccuracy;
+ }
+
+ public int getPowerRequirement() {
+ return mPowerRequirement;
+ }
+
+ public boolean hasMonetaryCost() {
+ return mHasMonetaryCost;
+ }
+
+ public boolean requiresCell() {
+ return mRequiresCell;
+ }
+
+ public boolean requiresNetwork() {
+ return mRequiresNetwork;
+ }
+
+ public boolean requiresSatellite() {
+ return mRequiresSatellite;
+ }
+
+ public boolean supportsAltitude() {
+ return mSupportsAltitude;
+ }
+
+ public boolean supportsBearing() {
+ return mSupportsBearing;
+ }
+
+ public boolean supportsSpeed() {
+ return mSupportsSpeed;
+ }
+
+ public boolean meetsCriteria(Criteria criteria) {
+ if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
+ (criteria.getAccuracy() < mAccuracy)) {
+ return false;
+ }
+ int criteriaPower = criteria.getPowerRequirement();
+ if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
+ (criteriaPower < mPowerRequirement)) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !mSupportsAltitude) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !mSupportsSpeed) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !mSupportsBearing) {
+ return false;
+ }
+ return true;
+ }
+
+ public void setLocation(Location l) {
+ mLocation.set(l);
+ mHasLocation = true;
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+
+ public void clearLocation() {
+ mHasLocation = false;
+ }
+
+ public void setStatus(int status, Bundle extras, long updateTime) {
+ mStatus = status;
+ mStatusUpdateTime = updateTime;
+ mExtras.clear();
+ if (extras != null) {
+ mExtras.putAll(extras);
+ }
+ mHasStatus = true;
+ }
+
+ public void clearStatus() {
+ mHasStatus = false;
+ mStatusUpdateTime = 0;
+ }
+
+ public String getInternalState() {
+ return null;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ }
+
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
+ public void setMinTime(long minTime) {
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ }
+
+ public void updateLocation(Location location) {
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
+
+ public void addListener(int uid) {
+ }
+
+ public void removeListener(int uid) {
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + mName);
+ pw.println(prefix + "mHasLocation=" + mHasLocation);
+ pw.println(prefix + "mLocation:");
+ mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
+ pw.println(prefix + "mHasStatus=" + mHasStatus);
+ pw.println(prefix + "mStatus=" + mStatus);
+ pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
+ pw.println(prefix + "mExtras=" + mExtras);
+ }
+}
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
new file mode 100644
index 0000000..7fc93f8
--- /dev/null
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.location.Criteria;
+import android.location.ILocationManager;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A passive location provider reports locations received from other providers
+ * for clients that want to listen passively without actually triggering
+ * location updates.
+ *
+ * {@hide}
+ */
+public class PassiveProvider implements LocationProviderInterface {
+
+ private static final String TAG = "PassiveProvider";
+
+ private final ILocationManager mLocationManager;
+ private boolean mTracking;
+
+ public PassiveProvider(ILocationManager locationManager) {
+ mLocationManager = locationManager;
+ }
+
+ public String getName() {
+ return LocationManager.PASSIVE_PROVIDER;
+ }
+
+ public boolean requiresNetwork() {
+ return false;
+ }
+
+ public boolean requiresSatellite() {
+ return false;
+ }
+
+ public boolean requiresCell() {
+ return false;
+ }
+
+ public boolean hasMonetaryCost() {
+ return false;
+ }
+
+ public boolean supportsAltitude() {
+ return false;
+ }
+
+ public boolean supportsSpeed() {
+ return false;
+ }
+
+ public boolean supportsBearing() {
+ return false;
+ }
+
+ public int getPowerRequirement() {
+ return -1;
+ }
+
+ public boolean meetsCriteria(Criteria criteria) {
+ // We do not want to match the special passive provider based on criteria.
+ return false;
+ }
+
+ public int getAccuracy() {
+ return -1;
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public void enable() {
+ }
+
+ public void disable() {
+ }
+
+ public int getStatus(Bundle extras) {
+ if (mTracking) {
+ return LocationProvider.AVAILABLE;
+ } else {
+ return LocationProvider.TEMPORARILY_UNAVAILABLE;
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return -1;
+ }
+
+ public String getInternalState() {
+ return null;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ mTracking = enable;
+ }
+
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
+ public void setMinTime(long minTime) {
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ }
+
+ public void updateLocation(Location location) {
+ if (mTracking) {
+ try {
+ // pass the location back to the location manager
+ mLocationManager.reportLocation(location, true);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
+
+ public void addListener(int uid) {
+ }
+
+ public void removeListener(int uid) {
+ }
+}
diff --git a/services/java/com/android/server/sip/SipHelper.java b/services/java/com/android/server/sip/SipHelper.java
new file mode 100644
index 0000000..83eeb84
--- /dev/null
+++ b/services/java/com/android/server/sip/SipHelper.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sip;
+
+import gov.nist.javax.sip.SipStackExt;
+import gov.nist.javax.sip.clientauthutils.AccountManager;
+import gov.nist.javax.sip.clientauthutils.AuthenticationHelper;
+
+import android.net.sip.SessionDescription;
+import android.net.sip.SipProfile;
+import android.util.Log;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.List;
+import javax.sip.ClientTransaction;
+import javax.sip.Dialog;
+import javax.sip.DialogTerminatedEvent;
+import javax.sip.InvalidArgumentException;
+import javax.sip.ListeningPoint;
+import javax.sip.PeerUnavailableException;
+import javax.sip.RequestEvent;
+import javax.sip.ResponseEvent;
+import javax.sip.ServerTransaction;
+import javax.sip.SipException;
+import javax.sip.SipFactory;
+import javax.sip.SipProvider;
+import javax.sip.SipStack;
+import javax.sip.Transaction;
+import javax.sip.TransactionAlreadyExistsException;
+import javax.sip.TransactionTerminatedEvent;
+import javax.sip.TransactionUnavailableException;
+import javax.sip.TransactionState;
+import javax.sip.address.Address;
+import javax.sip.address.AddressFactory;
+import javax.sip.address.SipURI;
+import javax.sip.header.CSeqHeader;
+import javax.sip.header.CallIdHeader;
+import javax.sip.header.ContactHeader;
+import javax.sip.header.FromHeader;
+import javax.sip.header.Header;
+import javax.sip.header.HeaderFactory;
+import javax.sip.header.MaxForwardsHeader;
+import javax.sip.header.ToHeader;
+import javax.sip.header.ViaHeader;
+import javax.sip.message.Message;
+import javax.sip.message.MessageFactory;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+/**
+ * Helper class for holding SIP stack related classes and for various low-level
+ * SIP tasks like sending messages.
+ */
+class SipHelper {
+ private static final String TAG = SipHelper.class.getSimpleName();
+
+ private SipStack mSipStack;
+ private SipProvider mSipProvider;
+ private AddressFactory mAddressFactory;
+ private HeaderFactory mHeaderFactory;
+ private MessageFactory mMessageFactory;
+
+ public SipHelper(SipStack sipStack, SipProvider sipProvider)
+ throws PeerUnavailableException {
+ mSipStack = sipStack;
+ mSipProvider = sipProvider;
+
+ SipFactory sipFactory = SipFactory.getInstance();
+ mAddressFactory = sipFactory.createAddressFactory();
+ mHeaderFactory = sipFactory.createHeaderFactory();
+ mMessageFactory = sipFactory.createMessageFactory();
+ }
+
+ private FromHeader createFromHeader(SipProfile profile, String tag)
+ throws ParseException {
+ return mHeaderFactory.createFromHeader(profile.getSipAddress(), tag);
+ }
+
+ private ToHeader createToHeader(SipProfile profile) throws ParseException {
+ return createToHeader(profile, null);
+ }
+
+ private ToHeader createToHeader(SipProfile profile, String tag)
+ throws ParseException {
+ return mHeaderFactory.createToHeader(profile.getSipAddress(), tag);
+ }
+
+ private CallIdHeader createCallIdHeader() {
+ return mSipProvider.getNewCallId();
+ }
+
+ private CSeqHeader createCSeqHeader(String method)
+ throws ParseException, InvalidArgumentException {
+ long sequence = (long) (Math.random() * 10000);
+ return mHeaderFactory.createCSeqHeader(sequence, method);
+ }
+
+ private MaxForwardsHeader createMaxForwardsHeader()
+ throws InvalidArgumentException {
+ return mHeaderFactory.createMaxForwardsHeader(70);
+ }
+
+ private MaxForwardsHeader createMaxForwardsHeader(int max)
+ throws InvalidArgumentException {
+ return mHeaderFactory.createMaxForwardsHeader(max);
+ }
+
+ private ListeningPoint getListeningPoint() throws SipException {
+ ListeningPoint lp = mSipProvider.getListeningPoint(ListeningPoint.UDP);
+ if (lp == null) lp = mSipProvider.getListeningPoint(ListeningPoint.TCP);
+ if (lp == null) {
+ ListeningPoint[] lps = mSipProvider.getListeningPoints();
+ if ((lps != null) && (lps.length > 0)) lp = lps[0];
+ }
+ if (lp == null) {
+ throw new SipException("no listening point is available");
+ }
+ return lp;
+ }
+
+ private List<ViaHeader> createViaHeaders()
+ throws ParseException, SipException {
+ List<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(1);
+ ListeningPoint lp = getListeningPoint();
+ ViaHeader viaHeader = mHeaderFactory.createViaHeader(lp.getIPAddress(),
+ lp.getPort(), lp.getTransport(), null);
+ viaHeader.setRPort();
+ viaHeaders.add(viaHeader);
+ return viaHeaders;
+ }
+
+ private ContactHeader createContactHeader(SipProfile profile)
+ throws ParseException, SipException {
+ ListeningPoint lp = getListeningPoint();
+ SipURI contactURI =
+ createSipUri(profile.getUserName(), profile.getProtocol(), lp);
+
+ Address contactAddress = mAddressFactory.createAddress(contactURI);
+ contactAddress.setDisplayName(profile.getDisplayName());
+
+ return mHeaderFactory.createContactHeader(contactAddress);
+ }
+
+ private ContactHeader createWildcardContactHeader() {
+ ContactHeader contactHeader = mHeaderFactory.createContactHeader();
+ contactHeader.setWildCard();
+ return contactHeader;
+ }
+
+ private SipURI createSipUri(String username, String transport,
+ ListeningPoint lp) throws ParseException {
+ SipURI uri = mAddressFactory.createSipURI(username, lp.getIPAddress());
+ try {
+ uri.setPort(lp.getPort());
+ uri.setTransportParam(transport);
+ } catch (InvalidArgumentException e) {
+ throw new RuntimeException(e);
+ }
+ return uri;
+ }
+
+ public ClientTransaction sendKeepAlive(SipProfile userProfile, String tag)
+ throws SipException {
+ try {
+ Request request = createRequest(Request.OPTIONS, userProfile, tag);
+
+ ClientTransaction clientTransaction =
+ mSipProvider.getNewClientTransaction(request);
+ clientTransaction.sendRequest();
+ return clientTransaction;
+ } catch (Exception e) {
+ throw new SipException("sendKeepAlive()", e);
+ }
+ }
+
+ public ClientTransaction sendRegister(SipProfile userProfile, String tag,
+ int expiry) throws SipException {
+ try {
+ Request request = createRequest(Request.REGISTER, userProfile, tag);
+ if (expiry == 0) {
+ // remove all previous registrations by wildcard
+ // rfc3261#section-10.2.2
+ request.addHeader(createWildcardContactHeader());
+ } else {
+ request.addHeader(createContactHeader(userProfile));
+ }
+ request.addHeader(mHeaderFactory.createExpiresHeader(expiry));
+
+ ClientTransaction clientTransaction =
+ mSipProvider.getNewClientTransaction(request);
+ clientTransaction.sendRequest();
+ return clientTransaction;
+ } catch (ParseException e) {
+ throw new SipException("sendRegister()", e);
+ }
+ }
+
+ private Request createRequest(String requestType, SipProfile userProfile,
+ String tag) throws ParseException, SipException {
+ FromHeader fromHeader = createFromHeader(userProfile, tag);
+ ToHeader toHeader = createToHeader(userProfile);
+ SipURI requestURI = mAddressFactory.createSipURI("sip:"
+ + userProfile.getSipDomain());
+ List<ViaHeader> viaHeaders = createViaHeaders();
+ CallIdHeader callIdHeader = createCallIdHeader();
+ CSeqHeader cSeqHeader = createCSeqHeader(requestType);
+ MaxForwardsHeader maxForwards = createMaxForwardsHeader();
+ Request request = mMessageFactory.createRequest(requestURI,
+ requestType, callIdHeader, cSeqHeader, fromHeader,
+ toHeader, viaHeaders, maxForwards);
+ Header userAgentHeader = mHeaderFactory.createHeader("User-Agent",
+ "SIPAUA/0.1.001");
+ request.addHeader(userAgentHeader);
+ return request;
+ }
+
+ public ClientTransaction handleChallenge(ResponseEvent responseEvent,
+ AccountManager accountManager) throws SipException {
+ AuthenticationHelper authenticationHelper =
+ ((SipStackExt) mSipStack).getAuthenticationHelper(
+ accountManager, mHeaderFactory);
+ ClientTransaction tid = responseEvent.getClientTransaction();
+ ClientTransaction ct = authenticationHelper.handleChallenge(
+ responseEvent.getResponse(), tid, mSipProvider, 5);
+ ct.sendRequest();
+ return ct;
+ }
+
+ public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,
+ SessionDescription sessionDescription, String tag)
+ throws SipException {
+ try {
+ FromHeader fromHeader = createFromHeader(caller, tag);
+ ToHeader toHeader = createToHeader(callee);
+ SipURI requestURI = callee.getUri();
+ List<ViaHeader> viaHeaders = createViaHeaders();
+ CallIdHeader callIdHeader = createCallIdHeader();
+ CSeqHeader cSeqHeader = createCSeqHeader(Request.INVITE);
+ MaxForwardsHeader maxForwards = createMaxForwardsHeader();
+
+ Request request = mMessageFactory.createRequest(requestURI,
+ Request.INVITE, callIdHeader, cSeqHeader, fromHeader,
+ toHeader, viaHeaders, maxForwards);
+
+ request.addHeader(createContactHeader(caller));
+ request.setContent(sessionDescription.getContent(),
+ mHeaderFactory.createContentTypeHeader(
+ "application", sessionDescription.getType()));
+
+ ClientTransaction clientTransaction =
+ mSipProvider.getNewClientTransaction(request);
+ clientTransaction.sendRequest();
+ return clientTransaction;
+ } catch (ParseException e) {
+ throw new SipException("sendInvite()", e);
+ }
+ }
+
+ public ClientTransaction sendReinvite(Dialog dialog,
+ SessionDescription sessionDescription) throws SipException {
+ try {
+ Request request = dialog.createRequest(Request.INVITE);
+ request.setContent(sessionDescription.getContent(),
+ mHeaderFactory.createContentTypeHeader(
+ "application", sessionDescription.getType()));
+
+ ClientTransaction clientTransaction =
+ mSipProvider.getNewClientTransaction(request);
+ dialog.sendRequest(clientTransaction);
+ return clientTransaction;
+ } catch (ParseException e) {
+ throw new SipException("sendReinvite()", e);
+ }
+ }
+
+ private ServerTransaction getServerTransaction(RequestEvent event)
+ throws SipException {
+ ServerTransaction transaction = event.getServerTransaction();
+ if (transaction == null) {
+ Request request = event.getRequest();
+ return mSipProvider.getNewServerTransaction(request);
+ } else {
+ return transaction;
+ }
+ }
+
+ /**
+ * @param event the INVITE request event
+ */
+ public ServerTransaction sendRinging(RequestEvent event, String tag)
+ throws SipException {
+ try {
+ Request request = event.getRequest();
+ ServerTransaction transaction = getServerTransaction(event);
+
+ Response response = mMessageFactory.createResponse(Response.RINGING,
+ request);
+
+ ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
+ toHeader.setTag(tag);
+ response.addHeader(toHeader);
+ transaction.sendResponse(response);
+ return transaction;
+ } catch (ParseException e) {
+ throw new SipException("sendRinging()", e);
+ }
+ }
+
+ /**
+ * @param event the INVITE request event
+ */
+ public ServerTransaction sendInviteOk(RequestEvent event,
+ SipProfile localProfile, SessionDescription sessionDescription,
+ ServerTransaction inviteTransaction)
+ throws SipException {
+ try {
+ Request request = event.getRequest();
+ Response response = mMessageFactory.createResponse(Response.OK,
+ request);
+ response.addHeader(createContactHeader(localProfile));
+ response.setContent(sessionDescription.getContent(),
+ mHeaderFactory.createContentTypeHeader(
+ "application", sessionDescription.getType()));
+
+ if (inviteTransaction == null) {
+ inviteTransaction = getServerTransaction(event);
+ }
+ if (inviteTransaction.getState() != TransactionState.COMPLETED) {
+ inviteTransaction.sendResponse(response);
+ }
+
+ return inviteTransaction;
+ } catch (ParseException e) {
+ throw new SipException("sendInviteOk()", e);
+ }
+ }
+
+ public void sendInviteBusyHere(RequestEvent event,
+ ServerTransaction inviteTransaction) throws SipException {
+ try {
+ Request request = event.getRequest();
+ Response response = mMessageFactory.createResponse(
+ Response.BUSY_HERE, request);
+
+ if (inviteTransaction.getState() != TransactionState.COMPLETED) {
+ inviteTransaction.sendResponse(response);
+ }
+ } catch (ParseException e) {
+ throw new SipException("sendInviteBusyHere()", e);
+ }
+ }
+
+ /**
+ * @param event the INVITE ACK request event
+ */
+ public void sendInviteAck(ResponseEvent event, Dialog dialog)
+ throws SipException {
+ Response response = event.getResponse();
+ long cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME))
+ .getSeqNumber();
+ dialog.sendAck(dialog.createAck(cseq));
+ }
+
+ public void sendBye(Dialog dialog) throws SipException {
+ Request byeRequest = dialog.createRequest(Request.BYE);
+ Log.d(TAG, "send BYE: " + byeRequest);
+ dialog.sendRequest(mSipProvider.getNewClientTransaction(byeRequest));
+ }
+
+ public void sendCancel(ClientTransaction inviteTransaction)
+ throws SipException {
+ Request cancelRequest = inviteTransaction.createCancel();
+ mSipProvider.getNewClientTransaction(cancelRequest).sendRequest();
+ }
+
+ public void sendResponse(RequestEvent event, int responseCode)
+ throws SipException {
+ try {
+ getServerTransaction(event).sendResponse(
+ mMessageFactory.createResponse(
+ responseCode, event.getRequest()));
+ } catch (ParseException e) {
+ throw new SipException("sendResponse()", e);
+ }
+ }
+
+ public void sendInviteRequestTerminated(Request inviteRequest,
+ ServerTransaction inviteTransaction) throws SipException {
+ try {
+ inviteTransaction.sendResponse(mMessageFactory.createResponse(
+ Response.REQUEST_TERMINATED, inviteRequest));
+ } catch (ParseException e) {
+ throw new SipException("sendInviteRequestTerminated()", e);
+ }
+ }
+
+ public static String getCallId(EventObject event) {
+ if (event == null) return null;
+ if (event instanceof RequestEvent) {
+ return getCallId(((RequestEvent) event).getRequest());
+ } else if (event instanceof ResponseEvent) {
+ return getCallId(((ResponseEvent) event).getResponse());
+ } else if (event instanceof DialogTerminatedEvent) {
+ Dialog dialog = ((DialogTerminatedEvent) event).getDialog();
+ return getCallId(((DialogTerminatedEvent) event).getDialog());
+ } else if (event instanceof TransactionTerminatedEvent) {
+ TransactionTerminatedEvent e = (TransactionTerminatedEvent) event;
+ return getCallId(e.isServerTransaction()
+ ? e.getServerTransaction()
+ : e.getClientTransaction());
+ } else {
+ Object source = event.getSource();
+ if (source instanceof Transaction) {
+ return getCallId(((Transaction) source));
+ } else if (source instanceof Dialog) {
+ return getCallId((Dialog) source);
+ }
+ }
+ return "";
+ }
+
+ public static String getCallId(Transaction transaction) {
+ return ((transaction != null) ? getCallId(transaction.getRequest())
+ : "");
+ }
+
+ private static String getCallId(Message message) {
+ CallIdHeader callIdHeader =
+ (CallIdHeader) message.getHeader(CallIdHeader.NAME);
+ return callIdHeader.getCallId();
+ }
+
+ private static String getCallId(Dialog dialog) {
+ return dialog.getCallId().getCallId();
+ }
+}
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
new file mode 100644
index 0000000..3dcaff6
--- /dev/null
+++ b/services/java/com/android/server/sip/SipService.java
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sip;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.sip.ISipService;
+import android.net.sip.ISipSession;
+import android.net.sip.ISipSessionListener;
+import android.net.sip.SipManager;
+import android.net.sip.SipProfile;
+import android.net.sip.SipSessionAdapter;
+import android.net.sip.SipSessionState;
+import android.net.wifi.WifiManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+import javax.sip.SipException;
+
+/**
+ */
+public final class SipService extends ISipService.Stub {
+ private static final String TAG = "SipService";
+ private static final int EXPIRY_TIME = 3600;
+ private static final int SHORT_EXPIRY_TIME = 10;
+ private static final int MIN_EXPIRY_TIME = 60;
+
+ private Context mContext;
+ private String mLocalIp;
+ private String mNetworkType;
+ private boolean mConnected;
+ private WakeupTimer mTimer;
+ private WifiManager.WifiLock mWifiLock;
+
+ // SipProfile URI --> group
+ private Map<String, SipSessionGroupExt> mSipGroups =
+ new HashMap<String, SipSessionGroupExt>();
+
+ // session ID --> session
+ private Map<String, ISipSession> mPendingSessions =
+ new HashMap<String, ISipSession>();
+
+ private ConnectivityReceiver mConnectivityReceiver;
+
+ public SipService(Context context) {
+ Log.v(TAG, " service started!");
+ mContext = context;
+ mConnectivityReceiver = new ConnectivityReceiver();
+ context.registerReceiver(mConnectivityReceiver,
+ new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+
+ mTimer = new WakeupTimer(context);
+ }
+
+ public synchronized SipProfile[] getListOfProfiles() {
+ SipProfile[] profiles = new SipProfile[mSipGroups.size()];
+ int i = 0;
+ for (SipSessionGroupExt group : mSipGroups.values()) {
+ profiles[i++] = group.getLocalProfile();
+ }
+ return profiles;
+ }
+
+ public void open(SipProfile localProfile) {
+ localProfile.setCallingUid(Binder.getCallingUid());
+ if (localProfile.getAutoRegistration()) {
+ openToReceiveCalls(localProfile);
+ } else {
+ openToMakeCalls(localProfile);
+ }
+ }
+
+ private void openToMakeCalls(SipProfile localProfile) {
+ try {
+ createGroup(localProfile);
+ } catch (SipException e) {
+ Log.e(TAG, "openToMakeCalls()", e);
+ // TODO: how to send the exception back
+ }
+ }
+
+ private void openToReceiveCalls(SipProfile localProfile) {
+ open3(localProfile, SipManager.SIP_INCOMING_CALL_ACTION, null);
+ }
+
+ public synchronized void open3(SipProfile localProfile,
+ String incomingCallBroadcastAction, ISipSessionListener listener) {
+ localProfile.setCallingUid(Binder.getCallingUid());
+ if (TextUtils.isEmpty(incomingCallBroadcastAction)) {
+ throw new RuntimeException(
+ "empty broadcast action for incoming call");
+ }
+ Log.v(TAG, "open3: " + localProfile.getUriString() + ": "
+ + incomingCallBroadcastAction + ": " + listener);
+ try {
+ SipSessionGroupExt group = createGroup(localProfile,
+ incomingCallBroadcastAction, listener);
+ if (localProfile.getAutoRegistration()) {
+ group.openToReceiveCalls();
+ if (isWifiOn()) grabWifiLock();
+ }
+ } catch (SipException e) {
+ Log.e(TAG, "openToReceiveCalls()", e);
+ // TODO: how to send the exception back
+ }
+ }
+
+ public synchronized void close(String localProfileUri) {
+ SipSessionGroupExt group = mSipGroups.remove(localProfileUri);
+ if (group != null) {
+ notifyProfileRemoved(group.getLocalProfile());
+ group.closeToNotReceiveCalls();
+ if (isWifiOn() && !anyOpened()) releaseWifiLock();
+ }
+ }
+
+ public synchronized boolean isOpened(String localProfileUri) {
+ SipSessionGroupExt group = mSipGroups.get(localProfileUri);
+ return ((group != null) ? group.isOpened() : false);
+ }
+
+ public synchronized boolean isRegistered(String localProfileUri) {
+ SipSessionGroupExt group = mSipGroups.get(localProfileUri);
+ return ((group != null) ? group.isRegistered() : false);
+ }
+
+ public synchronized void setRegistrationListener(String localProfileUri,
+ ISipSessionListener listener) {
+ SipSessionGroupExt group = mSipGroups.get(localProfileUri);
+ if (group != null) group.setListener(listener);
+ }
+
+ public synchronized ISipSession createSession(SipProfile localProfile,
+ ISipSessionListener listener) {
+ localProfile.setCallingUid(Binder.getCallingUid());
+ if (!mConnected) return null;
+ try {
+ SipSessionGroupExt group = createGroup(localProfile);
+ return group.createSession(listener);
+ } catch (SipException e) {
+ Log.w(TAG, "createSession()", e);
+ return null;
+ }
+ }
+
+ public synchronized ISipSession getPendingSession(String callId) {
+ if (callId == null) return null;
+ return mPendingSessions.get(callId);
+ }
+
+ private String determineLocalIp() {
+ try {
+ DatagramSocket s = new DatagramSocket();
+ s.connect(InetAddress.getByName("192.168.1.1"), 80);
+ return s.getLocalAddress().getHostAddress();
+ } catch (IOException e) {
+ Log.w(TAG, "determineLocalIp()", e);
+ // dont do anything; there should be a connectivity change going
+ return null;
+ }
+ }
+
+ private SipSessionGroupExt createGroup(SipProfile localProfile)
+ throws SipException {
+ String key = localProfile.getUriString();
+ SipSessionGroupExt group = mSipGroups.get(key);
+ if (group == null) {
+ group = new SipSessionGroupExt(localProfile, null, null);
+ mSipGroups.put(key, group);
+ notifyProfileAdded(localProfile);
+ }
+ return group;
+ }
+
+ private SipSessionGroupExt createGroup(SipProfile localProfile,
+ String incomingCallBroadcastAction, ISipSessionListener listener)
+ throws SipException {
+ String key = localProfile.getUriString();
+ SipSessionGroupExt group = mSipGroups.get(key);
+ if (group != null) {
+ group.setIncomingCallBroadcastAction(
+ incomingCallBroadcastAction);
+ group.setListener(listener);
+ } else {
+ group = new SipSessionGroupExt(localProfile,
+ incomingCallBroadcastAction, listener);
+ mSipGroups.put(key, group);
+ notifyProfileAdded(localProfile);
+ }
+ return group;
+ }
+
+ private void notifyProfileAdded(SipProfile localProfile) {
+ Log.d(TAG, "notify: profile added: " + localProfile);
+ Intent intent = new Intent(SipManager.SIP_ADD_PHONE_ACTION);
+ intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
+ mContext.sendBroadcast(intent);
+ }
+
+ private void notifyProfileRemoved(SipProfile localProfile) {
+ Log.d(TAG, "notify: profile removed: " + localProfile);
+ Intent intent = new Intent(SipManager.SIP_REMOVE_PHONE_ACTION);
+ intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
+ mContext.sendBroadcast(intent);
+ }
+
+ private boolean anyOpened() {
+ for (SipSessionGroupExt group : mSipGroups.values()) {
+ if (group.isOpened()) return true;
+ }
+ return false;
+ }
+
+ private void grabWifiLock() {
+ if (mWifiLock == null) {
+ Log.v(TAG, "acquire wifi lock");
+ mWifiLock = ((WifiManager)
+ mContext.getSystemService(Context.WIFI_SERVICE))
+ .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
+ mWifiLock.acquire();
+ }
+ }
+
+ private void releaseWifiLock() {
+ if (mWifiLock != null) {
+ Log.v(TAG, "release wifi lock");
+ mWifiLock.release();
+ mWifiLock = null;
+ }
+ }
+
+ private boolean isWifiOn() {
+ return "WIFI".equalsIgnoreCase(mNetworkType);
+ //return (mConnected && "WIFI".equalsIgnoreCase(mNetworkType));
+ }
+
+ private synchronized void onConnectivityChanged(
+ String type, boolean connected) {
+ Log.v(TAG, "onConnectivityChanged(): "
+ + mNetworkType + (mConnected? " CONNECTED" : " DISCONNECTED")
+ + " --> " + type + (connected? " CONNECTED" : " DISCONNECTED"));
+
+ boolean sameType = type.equals(mNetworkType);
+ if (!sameType && !connected) return;
+
+ boolean wasWifi = "WIFI".equalsIgnoreCase(mNetworkType);
+ boolean isWifi = "WIFI".equalsIgnoreCase(type);
+ boolean wifiOff = (isWifi && !connected) || (wasWifi && !sameType);
+ boolean wifiOn = isWifi && connected;
+ if (wifiOff) {
+ releaseWifiLock();
+ } else if (wifiOn) {
+ if (anyOpened()) grabWifiLock();
+ }
+
+ try {
+ boolean wasConnected = mConnected;
+ mNetworkType = type;
+ mConnected = connected;
+
+ if (wasConnected) {
+ mLocalIp = null;
+ for (SipSessionGroupExt group : mSipGroups.values()) {
+ group.onConnectivityChanged(false);
+ }
+ }
+
+ if (connected) {
+ mLocalIp = determineLocalIp();
+ for (SipSessionGroupExt group : mSipGroups.values()) {
+ group.onConnectivityChanged(true);
+ }
+ }
+
+ } catch (SipException e) {
+ Log.e(TAG, "onConnectivityChanged()", e);
+ }
+ }
+
+ private synchronized void addPendingSession(ISipSession session) {
+ try {
+ mPendingSessions.put(session.getCallId(), session);
+ } catch (RemoteException e) {
+ // should not happen with a local call
+ Log.e(TAG, "addPendingSession()", e);
+ }
+ }
+
+ private class SipSessionGroupExt extends SipSessionAdapter {
+ private SipSessionGroup mSipGroup;
+ private String mIncomingCallBroadcastAction;
+ private boolean mOpened;
+
+ private AutoRegistrationProcess mAutoRegistration =
+ new AutoRegistrationProcess();
+
+ public SipSessionGroupExt(SipProfile localProfile,
+ String incomingCallBroadcastAction,
+ ISipSessionListener listener) throws SipException {
+ String password = localProfile.getPassword();
+ SipProfile p = duplicate(localProfile);
+ mSipGroup = createSipSessionGroup(mLocalIp, p, password);
+ mIncomingCallBroadcastAction = incomingCallBroadcastAction;
+ mAutoRegistration.setListener(listener);
+ }
+
+ public SipProfile getLocalProfile() {
+ return mSipGroup.getLocalProfile();
+ }
+
+ // network connectivity is tricky because network can be disconnected
+ // at any instant so need to deal with exceptions carefully even when
+ // you think you are connected
+ private SipSessionGroup createSipSessionGroup(String localIp,
+ SipProfile localProfile, String password) throws SipException {
+ try {
+ return new SipSessionGroup(localIp, localProfile, password);
+ } catch (IOException e) {
+ // network disconnected
+ Log.w(TAG, "createSipSessionGroup(): network disconnected?");
+ if (localIp != null) {
+ return createSipSessionGroup(null, localProfile, password);
+ } else {
+ // recursive
+ Log.wtf(TAG, "impossible!");
+ throw new RuntimeException("createSipSessionGroup");
+ }
+ }
+ }
+
+ private SipProfile duplicate(SipProfile p) {
+ try {
+ return new SipProfile.Builder(p).setPassword("*").build();
+ } catch (Exception e) {
+ Log.wtf(TAG, "duplicate()", e);
+ throw new RuntimeException("duplicate profile", e);
+ }
+ }
+
+ public void setListener(ISipSessionListener listener) {
+ mAutoRegistration.setListener(listener);
+ }
+
+ public void setIncomingCallBroadcastAction(String action) {
+ mIncomingCallBroadcastAction = action;
+ }
+
+ public void openToReceiveCalls() throws SipException {
+ mOpened = true;
+ if (mConnected) {
+ mSipGroup.openToReceiveCalls(this);
+ mAutoRegistration.start(mSipGroup);
+ }
+ Log.v(TAG, " openToReceiveCalls: " + getUri() + ": "
+ + mIncomingCallBroadcastAction);
+ }
+
+ public void onConnectivityChanged(boolean connected)
+ throws SipException {
+ if (connected) {
+ resetGroup(mLocalIp);
+ if (mOpened) openToReceiveCalls();
+ } else {
+ // close mSipGroup but remember mOpened
+ Log.v(TAG, " close auto reg temporarily: " + getUri() + ": "
+ + mIncomingCallBroadcastAction);
+ mSipGroup.close();
+ mAutoRegistration.stop();
+ }
+ }
+
+ private void resetGroup(String localIp) throws SipException {
+ try {
+ mSipGroup.reset(localIp);
+ } catch (IOException e) {
+ // network disconnected
+ Log.w(TAG, "resetGroup(): network disconnected?");
+ if (localIp != null) {
+ resetGroup(null); // reset w/o local IP
+ } else {
+ // recursive
+ Log.wtf(TAG, "impossible!");
+ throw new RuntimeException("resetGroup");
+ }
+ }
+ }
+
+ public void closeToNotReceiveCalls() {
+ mOpened = false;
+ mSipGroup.closeToNotReceiveCalls();
+ mAutoRegistration.stop();
+ Log.v(TAG, " close: " + getUri() + ": "
+ + mIncomingCallBroadcastAction);
+ }
+
+ public ISipSession createSession(ISipSessionListener listener) {
+ return mSipGroup.createSession(listener);
+ }
+
+ @Override
+ public void onRinging(ISipSession session, SipProfile caller,
+ byte[] sessionDescription) {
+ synchronized (SipService.this) {
+ try {
+ if (!isRegistered()) {
+ session.endCall();
+ return;
+ }
+
+ // send out incoming call broadcast
+ Log.d(TAG, " ringing~~ " + getUri() + ": " + caller.getUri()
+ + ": " + session.getCallId());
+ addPendingSession(session);
+ Intent intent = SipManager.createIncomingCallBroadcast(
+ mIncomingCallBroadcastAction, session.getCallId(),
+ sessionDescription);
+ Log.d(TAG, " send out intent: " + intent);
+ mContext.sendBroadcast(intent);
+ } catch (RemoteException e) {
+ // should never happen with a local call
+ Log.e(TAG, "processCall()", e);
+ }
+ }
+ }
+
+ @Override
+ public void onError(ISipSession session, String errorClass,
+ String message) {
+ Log.v(TAG, "sip session error: " + errorClass + ": " + message);
+ }
+
+ public boolean isOpened() {
+ return mOpened;
+ }
+
+ public boolean isRegistered() {
+ return mAutoRegistration.isRegistered();
+ }
+
+ private String getUri() {
+ return mSipGroup.getLocalProfileUri();
+ }
+ }
+
+ private class KeepAliveProcess implements Runnable {
+ private static final String TAG = "\\KEEPALIVE/";
+ private static final int INTERVAL = 15;
+ private SipSessionGroup.SipSessionImpl mSession;
+
+ public KeepAliveProcess(SipSessionGroup.SipSessionImpl session) {
+ mSession = session;
+ }
+
+ public void start() {
+ mTimer.set(INTERVAL * 1000, this);
+ }
+
+ public void run() {
+ synchronized (SipService.this) {
+ SipSessionGroup.SipSessionImpl session = mSession.duplicate();
+ Log.d(TAG, " ~~~ keepalive");
+ mTimer.cancel(this);
+ session.sendKeepAlive();
+ if (session.isReRegisterRequired()) {
+ mSession.register(EXPIRY_TIME);
+ } else {
+ mTimer.set(INTERVAL * 1000, this);
+ }
+ }
+ }
+
+ public void stop() {
+ mTimer.cancel(this);
+ }
+ }
+
+ private class AutoRegistrationProcess extends SipSessionAdapter
+ implements Runnable {
+ private SipSessionGroup.SipSessionImpl mSession;
+ private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
+ private KeepAliveProcess mKeepAliveProcess;
+ private int mBackoff = 1;
+ private boolean mRegistered;
+ private long mExpiryTime;
+
+ private String getAction() {
+ return toString();
+ }
+
+ public void start(SipSessionGroup group) {
+ if (mSession == null) {
+ mBackoff = 1;
+ mSession = (SipSessionGroup.SipSessionImpl)
+ group.createSession(this);
+ // return right away if no active network connection.
+ if (mSession == null) return;
+
+ // start unregistration to clear up old registration at server
+ // TODO: when rfc5626 is deployed, use reg-id and sip.instance
+ // in registration to avoid adding duplicate entries to server
+ mSession.unregister();
+ Log.v(TAG, "start AutoRegistrationProcess for "
+ + mSession.getLocalProfile().getUriString());
+ }
+ }
+
+ public void stop() {
+ if (mSession == null) return;
+ if (mConnected) mSession.unregister();
+ mTimer.cancel(this);
+ if (mKeepAliveProcess != null) {
+ mKeepAliveProcess.stop();
+ mKeepAliveProcess = null;
+ }
+ mSession = null;
+ mRegistered = false;
+ }
+
+ private boolean isStopped() {
+ return (mSession == null);
+ }
+
+ public void setListener(ISipSessionListener listener) {
+ Log.v(TAG, "setListener(): " + listener);
+ mProxy.setListener(listener);
+ if (mSession == null) return;
+
+ try {
+ if ((mSession != null) && SipSessionState.REGISTERING.equals(
+ mSession.getState())) {
+ mProxy.onRegistering(mSession);
+ } else if (mRegistered) {
+ int duration = (int)
+ (mExpiryTime - SystemClock.elapsedRealtime());
+ mProxy.onRegistrationDone(mSession, duration);
+ }
+ } catch (Throwable t) {
+ Log.w(TAG, "setListener(): " + t);
+ }
+ }
+
+ public boolean isRegistered() {
+ return mRegistered;
+ }
+
+ public void run() {
+ Log.v(TAG, " ~~~ registering");
+ synchronized (SipService.this) {
+ if (mConnected && !isStopped()) mSession.register(EXPIRY_TIME);
+ }
+ }
+
+ private boolean isBehindNAT(String address) {
+ try {
+ byte[] d = InetAddress.getByName(address).getAddress();
+ if ((d[0] == 10) ||
+ (((0x000000FF & ((int)d[0])) == 172) &&
+ ((0x000000F0 & ((int)d[1])) == 16)) ||
+ (((0x000000FF & ((int)d[0])) == 192) &&
+ ((0x000000FF & ((int)d[1])) == 168))) {
+ return true;
+ }
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "isBehindAT()" + address, e);
+ }
+ return false;
+ }
+
+ private void restart(int duration) {
+ Log.v(TAG, "Refresh registration " + duration + "s later.");
+ mTimer.cancel(this);
+ mTimer.set(duration * 1000, this);
+ }
+
+ private int backoffDuration() {
+ int duration = SHORT_EXPIRY_TIME * mBackoff;
+ if (duration > 3600) {
+ duration = 3600;
+ } else {
+ mBackoff *= 2;
+ }
+ return duration;
+ }
+
+ @Override
+ public void onRegistering(ISipSession session) {
+ Log.v(TAG, "onRegistering(): " + session + ": " + mSession);
+ synchronized (SipService.this) {
+ if (!isStopped() && (session != mSession)) return;
+ mRegistered = false;
+ try {
+ mProxy.onRegistering(session);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRegistering()", t);
+ }
+ }
+ }
+
+ @Override
+ public void onRegistrationDone(ISipSession session, int duration) {
+ Log.v(TAG, "onRegistrationDone(): " + session + ": " + mSession);
+ synchronized (SipService.this) {
+ if (!isStopped() && (session != mSession)) return;
+ try {
+ mProxy.onRegistrationDone(session, duration);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRegistrationDone()", t);
+ }
+ if (isStopped()) return;
+
+ if (duration > 0) {
+ mSession.clearReRegisterRequired();
+ mExpiryTime = SystemClock.elapsedRealtime()
+ + (duration * 1000);
+
+ if (!mRegistered) {
+ mRegistered = true;
+ // allow some overlap to avoid call drop during renew
+ duration -= MIN_EXPIRY_TIME;
+ if (duration < MIN_EXPIRY_TIME) {
+ duration = MIN_EXPIRY_TIME;
+ }
+ restart(duration);
+
+ if (isBehindNAT(mLocalIp) ||
+ mSession.getLocalProfile().getSendKeepAlive()) {
+ if (mKeepAliveProcess == null) {
+ mKeepAliveProcess =
+ new KeepAliveProcess(mSession);
+ }
+ mKeepAliveProcess.start();
+ }
+ }
+ } else {
+ mRegistered = false;
+ mExpiryTime = -1L;
+ Log.v(TAG, "Refresh registration immediately");
+ run();
+ }
+ }
+ }
+
+ @Override
+ public void onRegistrationFailed(ISipSession session, String className,
+ String message) {
+ Log.v(TAG, "onRegistrationFailed(): " + session + ": " + mSession
+ + ": " + className + ": " + message);
+ synchronized (SipService.this) {
+ if (!isStopped() && (session != mSession)) return;
+ try {
+ mProxy.onRegistrationFailed(session, className, message);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRegistrationFailed(): " + t);
+ }
+
+ if (!isStopped()) onError();
+ }
+ }
+
+ @Override
+ public void onRegistrationTimeout(ISipSession session) {
+ Log.v(TAG, "onRegistrationTimeout(): " + session + ": " + mSession);
+ synchronized (SipService.this) {
+ if (!isStopped() && (session != mSession)) return;
+ try {
+ mProxy.onRegistrationTimeout(session);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRegistrationTimeout(): " + t);
+ }
+
+ if (!isStopped()) {
+ mRegistered = false;
+ onError();
+ }
+ }
+ }
+
+ private void onError() {
+ mRegistered = false;
+ restart(backoffDuration());
+ if (mKeepAliveProcess != null) {
+ mKeepAliveProcess.stop();
+ mKeepAliveProcess = null;
+ }
+ }
+ }
+
+ private class ConnectivityReceiver extends BroadcastReceiver {
+ private Timer mTimer = new Timer();
+ private MyTimerTask mTask;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ Bundle b = intent.getExtras();
+ if (b != null) {
+ NetworkInfo netInfo = (NetworkInfo)
+ b.get(ConnectivityManager.EXTRA_NETWORK_INFO);
+ String type = netInfo.getTypeName();
+ NetworkInfo.State state = netInfo.getState();
+ if (state == NetworkInfo.State.CONNECTED) {
+ Log.v(TAG, "Connectivity alert: CONNECTED " + type);
+ onChanged(type, true);
+ } else if (state == NetworkInfo.State.DISCONNECTED) {
+ Log.v(TAG, "Connectivity alert: DISCONNECTED " + type);
+ onChanged(type, false);
+ } else {
+ Log.d(TAG, "Connectivity alert not processed: " + state
+ + " " + type);
+ }
+ }
+ }
+ }
+
+ private void onChanged(String type, boolean connected) {
+ synchronized (SipService.this) {
+ // When turning on WIFI, it needs some time for network
+ // connectivity to get stabile so we defer good news (because
+ // we want to skip the interim ones) but deliver bad news
+ // immediately
+ if (connected) {
+ if (mTask != null) mTask.cancel();
+ mTask = new MyTimerTask(type, connected);
+ mTimer.schedule(mTask, 3 * 1000L);
+ // TODO: hold wakup lock so that we can finish change before
+ // the device goes to sleep
+ } else {
+ if ((mTask != null) && mTask.mNetworkType.equals(type)) {
+ mTask.cancel();
+ }
+ onConnectivityChanged(type, false);
+ }
+ }
+ }
+
+ private class MyTimerTask extends TimerTask {
+ private boolean mConnected;
+ private String mNetworkType;
+
+ public MyTimerTask(String type, boolean connected) {
+ mNetworkType = type;
+ mConnected = connected;
+ }
+
+ @Override
+ public void run() {
+ synchronized (SipService.this) {
+ if (mTask != this) {
+ Log.w(TAG, " unexpected task: " + mNetworkType
+ + (mConnected ? " CONNECTED" : "DISCONNECTED"));
+ return;
+ }
+ mTask = null;
+ Log.v(TAG, " deliver change for " + mNetworkType
+ + (mConnected ? " CONNECTED" : "DISCONNECTED"));
+ onConnectivityChanged(mNetworkType, mConnected);
+ }
+ }
+ }
+ }
+
+ // TODO: clean up pending SipSession(s) periodically
+
+
+ /**
+ * Timer that can schedule events to occur even when the device is in sleep.
+ * Only used internally in this package.
+ */
+ class WakeupTimer extends BroadcastReceiver {
+ private static final String TAG = "_SIP.WkTimer_";
+ private static final String TRIGGER_TIME = "TriggerTime";
+
+ private Context mContext;
+ private AlarmManager mAlarmManager;
+
+ // runnable --> time to execute in SystemClock
+ private TreeSet<MyEvent> mEventQueue =
+ new TreeSet<MyEvent>(new MyEventComparator());
+
+ private PendingIntent mPendingIntent;
+
+ public WakeupTimer(Context context) {
+ mContext = context;
+ mAlarmManager = (AlarmManager)
+ context.getSystemService(Context.ALARM_SERVICE);
+
+ IntentFilter filter = new IntentFilter(getAction());
+ context.registerReceiver(this, filter);
+ }
+
+ /**
+ * Stops the timer. No event can be scheduled after this method is called.
+ */
+ public synchronized void stop() {
+ mContext.unregisterReceiver(this);
+ if (mPendingIntent != null) {
+ mAlarmManager.cancel(mPendingIntent);
+ mPendingIntent = null;
+ }
+ mEventQueue.clear();
+ mEventQueue = null;
+ }
+
+ private synchronized boolean stopped() {
+ if (mEventQueue == null) {
+ Log.w(TAG, "Timer stopped");
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void cancelAlarm() {
+ mAlarmManager.cancel(mPendingIntent);
+ mPendingIntent = null;
+ }
+
+ private void recalculatePeriods() {
+ if (mEventQueue.isEmpty()) return;
+
+ MyEvent firstEvent = mEventQueue.first();
+ int minPeriod = firstEvent.mMaxPeriod;
+ long minTriggerTime = firstEvent.mTriggerTime;
+ for (MyEvent e : mEventQueue) {
+ e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod;
+ int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod
+ - minTriggerTime);
+ interval = interval / minPeriod * minPeriod;
+ e.mTriggerTime = minTriggerTime + interval;
+ }
+ TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>(
+ mEventQueue.comparator());
+ newQueue.addAll((Collection<MyEvent>) mEventQueue);
+ mEventQueue.clear();
+ mEventQueue = newQueue;
+ Log.v(TAG, "queue re-calculated");
+ printQueue();
+ }
+
+ // Determines the period and the trigger time of the new event and insert it
+ // to the queue.
+ private void insertEvent(MyEvent event) {
+ long now = SystemClock.elapsedRealtime();
+ if (mEventQueue.isEmpty()) {
+ event.mTriggerTime = now + event.mPeriod;
+ mEventQueue.add(event);
+ return;
+ }
+ MyEvent firstEvent = mEventQueue.first();
+ int minPeriod = firstEvent.mPeriod;
+ if (minPeriod <= event.mMaxPeriod) {
+ event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod;
+ int interval = event.mMaxPeriod;
+ interval -= (int) (firstEvent.mTriggerTime - now);
+ interval = interval / minPeriod * minPeriod;
+ event.mTriggerTime = firstEvent.mTriggerTime + interval;
+ mEventQueue.add(event);
+ } else {
+ long triggerTime = now + event.mPeriod;
+ if (firstEvent.mTriggerTime < triggerTime) {
+ event.mTriggerTime = firstEvent.mTriggerTime;
+ event.mLastTriggerTime -= event.mPeriod;
+ } else {
+ event.mTriggerTime = triggerTime;
+ }
+ mEventQueue.add(event);
+ recalculatePeriods();
+ }
+ }
+
+ /**
+ * Sets a periodic timer.
+ *
+ * @param period the timer period; in milli-second
+ * @param callback is called back when the timer goes off; the same callback
+ * can be specified in multiple timer events
+ */
+ public synchronized void set(int period, Runnable callback) {
+ if (stopped()) return;
+
+ long now = SystemClock.elapsedRealtime();
+ MyEvent event = new MyEvent(period, callback, now);
+ insertEvent(event);
+
+ if (mEventQueue.first() == event) {
+ if (mEventQueue.size() > 1) cancelAlarm();
+ scheduleNext();
+ }
+
+ long triggerTime = event.mTriggerTime;
+ Log.v(TAG, " add event " + event + " scheduled at "
+ + showTime(triggerTime) + " at " + showTime(now)
+ + ", #events=" + mEventQueue.size());
+ printQueue();
+ }
+
+ /**
+ * Cancels all the timer events with the specified callback.
+ *
+ * @param callback the callback
+ */
+ public synchronized void cancel(Runnable callback) {
+ if (stopped() || mEventQueue.isEmpty()) return;
+ Log.d(TAG, "cancel:" + callback);
+
+ MyEvent firstEvent = mEventQueue.first();
+ for (Iterator<MyEvent> iter = mEventQueue.iterator();
+ iter.hasNext();) {
+ MyEvent event = iter.next();
+ if (event.mCallback == callback) {
+ iter.remove();
+ Log.d(TAG, " cancel found:" + event);
+ }
+ }
+ if (mEventQueue.isEmpty()) {
+ cancelAlarm();
+ } else if (mEventQueue.first() != firstEvent) {
+ cancelAlarm();
+ firstEvent = mEventQueue.first();
+ firstEvent.mPeriod = firstEvent.mMaxPeriod;
+ firstEvent.mTriggerTime = firstEvent.mLastTriggerTime
+ + firstEvent.mPeriod;
+ recalculatePeriods();
+ scheduleNext();
+ }
+ Log.d(TAG, "after cancel:");
+ printQueue();
+ }
+
+ private void scheduleNext() {
+ if (stopped() || mEventQueue.isEmpty()) return;
+
+ if (mPendingIntent != null) {
+ throw new RuntimeException("pendingIntent is not null!");
+ }
+
+ MyEvent event = mEventQueue.first();
+ Intent intent = new Intent(getAction());
+ intent.putExtra(TRIGGER_TIME, event.mTriggerTime);
+ PendingIntent pendingIntent = mPendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ event.mTriggerTime, pendingIntent);
+ }
+
+ @Override
+ public synchronized void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (getAction().equals(action)
+ && intent.getExtras().containsKey(TRIGGER_TIME)) {
+ mPendingIntent = null;
+ long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L);
+ execute(triggerTime);
+ } else {
+ Log.d(TAG, "unrecognized intent: " + intent);
+ }
+ }
+
+ private void printQueue() {
+ int count = 0;
+ for (MyEvent event : mEventQueue) {
+ Log.d(TAG, " " + event + ": scheduled at "
+ + showTime(event.mTriggerTime) + ": last at "
+ + showTime(event.mLastTriggerTime));
+ if (++count >= 5) break;
+ }
+ if (mEventQueue.size() > count) {
+ Log.d(TAG, " .....");
+ } else if (count == 0) {
+ Log.d(TAG, " <empty>");
+ }
+ }
+
+ private void execute(long triggerTime) {
+ Log.d(TAG, "time's up, triggerTime = " + showTime(triggerTime) + ": "
+ + mEventQueue.size());
+ if (stopped() || mEventQueue.isEmpty()) return;
+
+ for (MyEvent event : mEventQueue) {
+ if (event.mTriggerTime != triggerTime) break;
+ Log.d(TAG, "execute " + event);
+
+ event.mLastTriggerTime = event.mTriggerTime;
+ event.mTriggerTime += event.mPeriod;
+
+ // run the callback in a new thread to prevent deadlock
+ new Thread(event.mCallback).start();
+ }
+ Log.d(TAG, "after timeout execution");
+ printQueue();
+ scheduleNext();
+ }
+
+ private String getAction() {
+ return toString();
+ }
+
+ private String showTime(long time) {
+ int ms = (int) (time % 1000);
+ int s = (int) (time / 1000);
+ int m = s / 60;
+ s %= 60;
+ return String.format("%d.%d.%d", m, s, ms);
+ }
+ }
+
+ private static class MyEvent {
+ int mPeriod;
+ int mMaxPeriod;
+ long mTriggerTime;
+ long mLastTriggerTime;
+ Runnable mCallback;
+
+ MyEvent(int period, Runnable callback, long now) {
+ mPeriod = mMaxPeriod = period;
+ mCallback = callback;
+ mLastTriggerTime = now;
+ }
+
+ @Override
+ public String toString() {
+ String s = super.toString();
+ s = s.substring(s.indexOf("@"));
+ return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":"
+ + toString(mCallback);
+ }
+
+ private String toString(Object o) {
+ String s = o.toString();
+ int index = s.indexOf("$");
+ if (index > 0) s = s.substring(index + 1);
+ return s;
+ }
+ }
+
+ private static class MyEventComparator implements Comparator<MyEvent> {
+ public int compare(MyEvent e1, MyEvent e2) {
+ if (e1 == e2) return 0;
+ int diff = e1.mMaxPeriod - e2.mMaxPeriod;
+ if (diff == 0) diff = -1;
+ return diff;
+ }
+
+ public boolean equals(Object that) {
+ return (this == that);
+ }
+ }
+}
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
new file mode 100644
index 0000000..d33558b
--- /dev/null
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -0,0 +1,1080 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sip;
+
+import gov.nist.javax.sip.clientauthutils.AccountManager;
+import gov.nist.javax.sip.clientauthutils.UserCredentials;
+import gov.nist.javax.sip.header.SIPHeaderNames;
+import gov.nist.javax.sip.header.WWWAuthenticate;
+
+import android.net.sip.ISipSession;
+import android.net.sip.ISipSessionListener;
+import android.net.sip.SessionDescription;
+import android.net.sip.SipProfile;
+import android.net.sip.SipSessionAdapter;
+import android.net.sip.SipSessionState;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TooManyListenersException;
+
+import javax.sip.ClientTransaction;
+import javax.sip.Dialog;
+import javax.sip.DialogTerminatedEvent;
+import javax.sip.IOExceptionEvent;
+import javax.sip.InvalidArgumentException;
+import javax.sip.ListeningPoint;
+import javax.sip.RequestEvent;
+import javax.sip.ResponseEvent;
+import javax.sip.ServerTransaction;
+import javax.sip.SipException;
+import javax.sip.SipFactory;
+import javax.sip.SipListener;
+import javax.sip.SipProvider;
+import javax.sip.SipStack;
+import javax.sip.TimeoutEvent;
+import javax.sip.Transaction;
+import javax.sip.TransactionState;
+import javax.sip.TransactionTerminatedEvent;
+import javax.sip.address.Address;
+import javax.sip.address.SipURI;
+import javax.sip.header.CSeqHeader;
+import javax.sip.header.ExpiresHeader;
+import javax.sip.header.FromHeader;
+import javax.sip.header.MinExpiresHeader;
+import javax.sip.header.ViaHeader;
+import javax.sip.message.Message;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+/**
+ * Manages {@link ISipSession}'s for a SIP account.
+ */
+class SipSessionGroup implements SipListener {
+ private static final String TAG = "SipSession";
+ private static final String ANONYMOUS = "anonymous";
+ private static final int EXPIRY_TIME = 3600;
+
+ private static final EventObject DEREGISTER = new EventObject("Deregister");
+ private static final EventObject END_CALL = new EventObject("End call");
+ private static final EventObject HOLD_CALL = new EventObject("Hold call");
+ private static final EventObject CONTINUE_CALL
+ = new EventObject("Continue call");
+
+ private final SipProfile mLocalProfile;
+ private final String mPassword;
+
+ private SipStack mSipStack;
+ private SipHelper mSipHelper;
+ private String mLastNonce;
+ private int mRPort;
+
+ // session that processes INVITE requests
+ private SipSessionImpl mCallReceiverSession;
+ private String mLocalIp;
+
+ // call-id-to-SipSession map
+ private Map<String, SipSessionImpl> mSessionMap =
+ new HashMap<String, SipSessionImpl>();
+
+ /**
+ * @param myself the local profile with password crossed out
+ * @param password the password of the profile
+ * @throws IOException if cannot assign requested address
+ */
+ public SipSessionGroup(String localIp, SipProfile myself, String password)
+ throws SipException, IOException {
+ mLocalProfile = myself;
+ mPassword = password;
+ reset(localIp);
+ }
+
+ void reset(String localIp) throws SipException, IOException {
+ mLocalIp = localIp;
+ if (localIp == null) return;
+
+ SipProfile myself = mLocalProfile;
+ SipFactory sipFactory = SipFactory.getInstance();
+ Properties properties = new Properties();
+ properties.setProperty("javax.sip.STACK_NAME", getStackName());
+ String outboundProxy = myself.getProxyAddress();
+ if (!TextUtils.isEmpty(outboundProxy)) {
+ properties.setProperty("javax.sip.OUTBOUND_PROXY", outboundProxy
+ + ":" + myself.getPort() + "/" + myself.getProtocol());
+ }
+ SipStack stack = mSipStack = sipFactory.createSipStack(properties);
+
+ try {
+ SipProvider provider = stack.createSipProvider(
+ stack.createListeningPoint(localIp, allocateLocalPort(),
+ myself.getProtocol()));
+ provider.addSipListener(this);
+ mSipHelper = new SipHelper(stack, provider);
+ } catch (InvalidArgumentException e) {
+ throw new IOException(e.getMessage());
+ } catch (TooManyListenersException e) {
+ // must never happen
+ throw new SipException("SipSessionGroup constructor", e);
+ }
+ Log.d(TAG, " start stack for " + myself.getUriString());
+ stack.start();
+
+ mLastNonce = null;
+ mCallReceiverSession = null;
+ mSessionMap.clear();
+ }
+
+ public SipProfile getLocalProfile() {
+ return mLocalProfile;
+ }
+
+ public String getLocalProfileUri() {
+ return mLocalProfile.getUriString();
+ }
+
+ private String getStackName() {
+ return "stack" + System.currentTimeMillis();
+ }
+
+ public synchronized void close() {
+ Log.d(TAG, " close stack for " + mLocalProfile.getUriString());
+ mSessionMap.clear();
+ closeToNotReceiveCalls();
+ if (mSipStack != null) {
+ mSipStack.stop();
+ mSipStack = null;
+ mSipHelper = null;
+ }
+ }
+
+ public synchronized boolean isClosed() {
+ return (mSipStack == null);
+ }
+
+ // For internal use, require listener not to block in callbacks.
+ public synchronized void openToReceiveCalls(ISipSessionListener listener) {
+ if (mCallReceiverSession == null) {
+ mCallReceiverSession = new SipSessionCallReceiverImpl(listener);
+ } else {
+ mCallReceiverSession.setListener(listener);
+ }
+ }
+
+ public synchronized void closeToNotReceiveCalls() {
+ mCallReceiverSession = null;
+ }
+
+ public ISipSession createSession(ISipSessionListener listener) {
+ return (isClosed() ? null : new SipSessionImpl(listener));
+ }
+
+ private static int allocateLocalPort() throws SipException {
+ try {
+ DatagramSocket s = new DatagramSocket();
+ int localPort = s.getLocalPort();
+ s.close();
+ return localPort;
+ } catch (IOException e) {
+ throw new SipException("allocateLocalPort()", e);
+ }
+ }
+
+ private synchronized SipSessionImpl getSipSession(EventObject event) {
+ String key = SipHelper.getCallId(event);
+ Log.d(TAG, " sesssion key from event: " + key);
+ Log.d(TAG, " active sessions:");
+ for (String k : mSessionMap.keySet()) {
+ Log.d(TAG, " ..... '" + k + "': " + mSessionMap.get(k));
+ }
+ SipSessionImpl session = mSessionMap.get(key);
+ return ((session != null) ? session : mCallReceiverSession);
+ }
+
+ private synchronized void addSipSession(SipSessionImpl newSession) {
+ removeSipSession(newSession);
+ String key = newSession.getCallId();
+ Log.d(TAG, " +++++ add a session with key: '" + key + "'");
+ mSessionMap.put(key, newSession);
+ for (String k : mSessionMap.keySet()) {
+ Log.d(TAG, " ..... " + k + ": " + mSessionMap.get(k));
+ }
+ }
+
+ private synchronized void removeSipSession(SipSessionImpl session) {
+ if (session == mCallReceiverSession) return;
+ String key = session.getCallId();
+ SipSessionImpl s = mSessionMap.remove(key);
+ // sanity check
+ if ((s != null) && (s != session)) {
+ Log.w(TAG, "session " + session + " is not associated with key '"
+ + key + "'");
+ mSessionMap.put(key, s);
+ for (Map.Entry<String, SipSessionImpl> entry
+ : mSessionMap.entrySet()) {
+ if (entry.getValue() == s) {
+ key = entry.getKey();
+ mSessionMap.remove(key);
+ }
+ }
+ }
+ Log.d(TAG, " remove session " + session + " with key '" + key + "'");
+
+ for (String k : mSessionMap.keySet()) {
+ Log.d(TAG, " ..... " + k + ": " + mSessionMap.get(k));
+ }
+ }
+
+ public void processRequest(RequestEvent event) {
+ process(event);
+ }
+
+ public void processResponse(ResponseEvent event) {
+ process(event);
+ }
+
+ public void processIOException(IOExceptionEvent event) {
+ process(event);
+ }
+
+ public void processTimeout(TimeoutEvent event) {
+ process(event);
+ }
+
+ public void processTransactionTerminated(TransactionTerminatedEvent event) {
+ process(event);
+ }
+
+ public void processDialogTerminated(DialogTerminatedEvent event) {
+ process(event);
+ }
+
+ private synchronized void process(EventObject event) {
+ SipSessionImpl session = getSipSession(event);
+ try {
+ if ((session != null) && session.process(event)) {
+ Log.d(TAG, " ~~~~~ new state: " + session.mState);
+ } else {
+ Log.d(TAG, "event not processed: " + event);
+ }
+ } catch (Throwable e) {
+ Log.e(TAG, "event process error: " + event, e);
+ session.onError(e);
+ }
+ }
+
+ private class SipSessionCallReceiverImpl extends SipSessionImpl {
+ public SipSessionCallReceiverImpl(ISipSessionListener listener) {
+ super(listener);
+ }
+
+ public boolean process(EventObject evt) throws SipException {
+ Log.d(TAG, " ~~~~~ " + this + ": " + mState + ": processing "
+ + log(evt));
+ if (isRequestEvent(Request.INVITE, evt)) {
+ RequestEvent event = (RequestEvent) evt;
+ SipSessionImpl newSession = new SipSessionImpl(mProxy);
+ newSession.mServerTransaction = mSipHelper.sendRinging(event,
+ generateTag());
+ newSession.mDialog = newSession.mServerTransaction.getDialog();
+ newSession.mInviteReceived = event;
+ newSession.mPeerProfile = createPeerProfile(event.getRequest());
+ newSession.mState = SipSessionState.INCOMING_CALL;
+ newSession.mPeerSessionDescription =
+ event.getRequest().getRawContent();
+ addSipSession(newSession);
+ mProxy.onRinging(newSession, newSession.mPeerProfile,
+ newSession.mPeerSessionDescription);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ class SipSessionImpl extends ISipSession.Stub {
+ SipProfile mPeerProfile;
+ SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
+ SipSessionState mState = SipSessionState.READY_TO_CALL;
+ RequestEvent mInviteReceived;
+ Dialog mDialog;
+ ServerTransaction mServerTransaction;
+ ClientTransaction mClientTransaction;
+ byte[] mPeerSessionDescription;
+ boolean mInCall;
+ boolean mReRegisterFlag = false;
+
+ public SipSessionImpl(ISipSessionListener listener) {
+ setListener(listener);
+ }
+
+ SipSessionImpl duplicate() {
+ return new SipSessionImpl(mProxy.getListener());
+ }
+
+ private void reset() {
+ mInCall = false;
+ removeSipSession(this);
+ mPeerProfile = null;
+ mState = SipSessionState.READY_TO_CALL;
+ mInviteReceived = null;
+ mDialog = null;
+ mServerTransaction = null;
+ mClientTransaction = null;
+ mPeerSessionDescription = null;
+ }
+
+ public boolean isInCall() {
+ return mInCall;
+ }
+
+ public String getLocalIp() {
+ return mLocalIp;
+ }
+
+ public SipProfile getLocalProfile() {
+ return mLocalProfile;
+ }
+
+ public SipProfile getPeerProfile() {
+ return mPeerProfile;
+ }
+
+ public String getCallId() {
+ return SipHelper.getCallId(getTransaction());
+ }
+
+ private Transaction getTransaction() {
+ if (mClientTransaction != null) return mClientTransaction;
+ if (mServerTransaction != null) return mServerTransaction;
+ return null;
+ }
+
+ public String getState() {
+ return mState.toString();
+ }
+
+ public void setListener(ISipSessionListener listener) {
+ mProxy.setListener((listener instanceof SipSessionListenerProxy)
+ ? ((SipSessionListenerProxy) listener).getListener()
+ : listener);
+ }
+
+ // process the command in a new thread
+ private void doCommandAsync(final EventObject command) {
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ processCommand(command);
+ } catch (SipException e) {
+ // TODO: find a better way to do this
+ if ((command instanceof RegisterCommand)
+ || (command == DEREGISTER)) {
+ onRegistrationFailed(e);
+ } else {
+ onError(e);
+ }
+ }
+ }
+ }).start();
+ }
+
+ public void makeCall(SipProfile peerProfile,
+ SessionDescription sessionDescription) {
+ doCommandAsync(
+ new MakeCallCommand(peerProfile, sessionDescription));
+ }
+
+ public void answerCall(SessionDescription sessionDescription) {
+ try {
+ processCommand(
+ new MakeCallCommand(mPeerProfile, sessionDescription));
+ } catch (SipException e) {
+ onError(e);
+ }
+ }
+
+ public void endCall() {
+ doCommandAsync(END_CALL);
+ }
+
+ public void changeCall(SessionDescription sessionDescription) {
+ doCommandAsync(
+ new MakeCallCommand(mPeerProfile, sessionDescription));
+ }
+
+ public void register(int duration) {
+ doCommandAsync(new RegisterCommand(duration));
+ }
+
+ public void unregister() {
+ doCommandAsync(DEREGISTER);
+ }
+
+ public boolean isReRegisterRequired() {
+ return mReRegisterFlag;
+ }
+
+ public void clearReRegisterRequired() {
+ mReRegisterFlag = false;
+ }
+
+ public void sendKeepAlive() {
+ mState = SipSessionState.PINGING;
+ try {
+ processCommand(new OptionsCommand());
+ while (SipSessionState.PINGING.equals(mState)) {
+ Thread.sleep(1000);
+ }
+ } catch (SipException e) {
+ Log.e(TAG, "sendKeepAlive failed", e);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "sendKeepAlive interrupted", e);
+ }
+ }
+
+ private void processCommand(EventObject command) throws SipException {
+ if (!process(command)) {
+ throw new SipException("wrong state to execute: " + command);
+ }
+ }
+
+ protected String generateTag() {
+ // 32-bit randomness
+ return String.valueOf((long) (Math.random() * 0x100000000L));
+ }
+
+ public String toString() {
+ try {
+ String s = super.toString();
+ return s.substring(s.indexOf("@")) + ":" + mState;
+ } catch (Throwable e) {
+ return super.toString();
+ }
+ }
+
+ public boolean process(EventObject evt) throws SipException {
+ Log.d(TAG, " ~~~~~ " + this + ": " + mState + ": processing "
+ + log(evt));
+ synchronized (SipSessionGroup.this) {
+ if (isClosed()) return false;
+
+ Dialog dialog = null;
+ if (evt instanceof RequestEvent) {
+ dialog = ((RequestEvent) evt).getDialog();
+ } else if (evt instanceof ResponseEvent) {
+ dialog = ((ResponseEvent) evt).getDialog();
+ }
+ if (dialog != null) mDialog = dialog;
+
+ boolean processed;
+
+ switch (mState) {
+ case REGISTERING:
+ case DEREGISTERING:
+ processed = registeringToReady(evt);
+ break;
+ case PINGING:
+ processed = keepAliveProcess(evt);
+ break;
+ case READY_TO_CALL:
+ processed = readyForCall(evt);
+ break;
+ case INCOMING_CALL:
+ processed = incomingCall(evt);
+ break;
+ case INCOMING_CALL_ANSWERING:
+ processed = incomingCallToInCall(evt);
+ break;
+ case OUTGOING_CALL:
+ case OUTGOING_CALL_RING_BACK:
+ processed = outgoingCall(evt);
+ break;
+ case OUTGOING_CALL_CANCELING:
+ processed = outgoingCallToReady(evt);
+ break;
+ case IN_CALL:
+ processed = inCall(evt);
+ break;
+ default:
+ processed = false;
+ }
+ return (processed || processExceptions(evt));
+ }
+ }
+
+ private boolean processExceptions(EventObject evt) throws SipException {
+ if (isRequestEvent(Request.BYE, evt)) {
+ // terminate the call whenever a BYE is received
+ mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
+ endCallNormally();
+ return true;
+ } else if (isRequestEvent(Request.CANCEL, evt)) {
+ mSipHelper.sendResponse((RequestEvent) evt,
+ Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
+ return true;
+ } else if (evt instanceof TransactionTerminatedEvent) {
+ if (evt instanceof TimeoutEvent) {
+ processTimeout((TimeoutEvent) evt);
+ } else {
+ Log.d(TAG, "Transaction terminated:" + this);
+ if (!SipSessionState.IN_CALL.equals(mState)) {
+ removeSipSession(this);
+ }
+ return true;
+ }
+ return true;
+ } else if (evt instanceof DialogTerminatedEvent) {
+ processDialogTerminated((DialogTerminatedEvent) evt);
+ return true;
+ }
+ return false;
+ }
+
+ private void processDialogTerminated(DialogTerminatedEvent event) {
+ if (mDialog == event.getDialog()) {
+ onError(new SipException("dialog terminated"));
+ } else {
+ Log.d(TAG, "not the current dialog; current=" + mDialog
+ + ", terminated=" + event.getDialog());
+ }
+ }
+
+ private void processTimeout(TimeoutEvent event) {
+ Log.d(TAG, "processing Timeout..." + event);
+ Transaction current = event.isServerTransaction()
+ ? mServerTransaction
+ : mClientTransaction;
+ Transaction target = event.isServerTransaction()
+ ? event.getServerTransaction()
+ : event.getClientTransaction();
+
+ if ((current != target) && (mState != SipSessionState.PINGING)) {
+ Log.d(TAG, "not the current transaction; current=" + current
+ + ", timed out=" + target);
+ return;
+ }
+ switch (mState) {
+ case REGISTERING:
+ case DEREGISTERING:
+ reset();
+ mProxy.onRegistrationTimeout(this);
+ break;
+ case INCOMING_CALL:
+ case INCOMING_CALL_ANSWERING:
+ case OUTGOING_CALL_CANCELING:
+ endCallOnError(new SipException("timed out"));
+ break;
+ case PINGING:
+ reset();
+ mReRegisterFlag = true;
+ mState = SipSessionState.READY_TO_CALL;
+ break;
+
+ default:
+ // do nothing
+ break;
+ }
+ }
+
+ private int getExpiryTime(Response response) {
+ int expires = EXPIRY_TIME;
+ ExpiresHeader expiresHeader = (ExpiresHeader)
+ response.getHeader(ExpiresHeader.NAME);
+ if (expiresHeader != null) expires = expiresHeader.getExpires();
+ expiresHeader = (ExpiresHeader)
+ response.getHeader(MinExpiresHeader.NAME);
+ if (expiresHeader != null) {
+ expires = Math.max(expires, expiresHeader.getExpires());
+ }
+ return expires;
+ }
+
+ private boolean keepAliveProcess(EventObject evt) throws SipException {
+ if (evt instanceof OptionsCommand) {
+ mClientTransaction = mSipHelper.sendKeepAlive(mLocalProfile,
+ generateTag());
+ mDialog = mClientTransaction.getDialog();
+ addSipSession(this);
+ return true;
+ } else if (evt instanceof ResponseEvent) {
+ return parseOptionsResult(evt);
+ }
+ return false;
+ }
+
+ private boolean parseOptionsResult(EventObject evt) {
+ if (expectResponse(Request.OPTIONS, evt)) {
+ ResponseEvent event = (ResponseEvent) evt;
+ int rPort = getRPortFromResponse(event.getResponse());
+ if (rPort != -1) {
+ if (mRPort == 0) mRPort = rPort;
+ if (mRPort != rPort) {
+ mReRegisterFlag = true;
+ Log.w(TAG, String.format("rport is changed: %d <> %d",
+ mRPort, rPort));
+ mRPort = rPort;
+ } else {
+ Log.w(TAG, "rport is the same: " + rPort);
+ }
+ } else {
+ Log.w(TAG, "peer did not respect our rport request");
+ }
+ mState = SipSessionState.READY_TO_CALL;
+ return true;
+ }
+ return false;
+ }
+
+ private int getRPortFromResponse(Response response) {
+ ViaHeader viaHeader = (ViaHeader)(response.getHeader(
+ SIPHeaderNames.VIA));
+ return (viaHeader == null) ? -1 : viaHeader.getRPort();
+ }
+
+ private boolean registeringToReady(EventObject evt)
+ throws SipException {
+ if (expectResponse(Request.REGISTER, evt)) {
+ ResponseEvent event = (ResponseEvent) evt;
+ Response response = event.getResponse();
+
+ int statusCode = response.getStatusCode();
+ switch (statusCode) {
+ case Response.OK:
+ SipSessionState state = mState;
+ reset();
+ onRegistrationDone((state == SipSessionState.REGISTERING)
+ ? getExpiryTime(((ResponseEvent) evt).getResponse())
+ : -1);
+ mLastNonce = null;
+ mRPort = 0;
+ return true;
+ case Response.UNAUTHORIZED:
+ case Response.PROXY_AUTHENTICATION_REQUIRED:
+ String nonce = getNonceFromResponse(response);
+ if (((nonce != null) && nonce.equals(mLastNonce)) ||
+ (nonce == mLastNonce)) {
+ Log.v(TAG, "Incorrect username/password");
+ reset();
+ onRegistrationFailed(createCallbackException(response));
+ } else {
+ mSipHelper.handleChallenge(event, getAccountManager());
+ mLastNonce = nonce;
+ }
+ return true;
+ default:
+ if (statusCode >= 500) {
+ reset();
+ onRegistrationFailed(createCallbackException(response));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private AccountManager getAccountManager() {
+ return new AccountManager() {
+ public UserCredentials getCredentials(ClientTransaction
+ challengedTransaction, String realm) {
+ return new UserCredentials() {
+ public String getUserName() {
+ return mLocalProfile.getUserName();
+ }
+
+ public String getPassword() {
+ return mPassword;
+ }
+
+ public String getSipDomain() {
+ return mLocalProfile.getSipDomain();
+ }
+ };
+ }
+ };
+ }
+
+ private String getNonceFromResponse(Response response) {
+ WWWAuthenticate authHeader = (WWWAuthenticate)(response.getHeader(
+ SIPHeaderNames.WWW_AUTHENTICATE));
+ return (authHeader == null) ? null : authHeader.getNonce();
+ }
+
+ private boolean readyForCall(EventObject evt) throws SipException {
+ // expect MakeCallCommand, RegisterCommand, DEREGISTER
+ if (evt instanceof MakeCallCommand) {
+ MakeCallCommand cmd = (MakeCallCommand) evt;
+ mPeerProfile = cmd.getPeerProfile();
+ SessionDescription sessionDescription =
+ cmd.getSessionDescription();
+ mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
+ mPeerProfile, sessionDescription, generateTag());
+ mDialog = mClientTransaction.getDialog();
+ addSipSession(this);
+ mState = SipSessionState.OUTGOING_CALL;
+ mProxy.onCalling(this);
+ return true;
+ } else if (evt instanceof RegisterCommand) {
+ int duration = ((RegisterCommand) evt).getDuration();
+ mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
+ generateTag(), duration);
+ mDialog = mClientTransaction.getDialog();
+ addSipSession(this);
+ mState = SipSessionState.REGISTERING;
+ mProxy.onRegistering(this);
+ return true;
+ } else if (DEREGISTER == evt) {
+ mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
+ generateTag(), 0);
+ mDialog = mClientTransaction.getDialog();
+ addSipSession(this);
+ mState = SipSessionState.DEREGISTERING;
+ mProxy.onRegistering(this);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean incomingCall(EventObject evt) throws SipException {
+ // expect MakeCallCommand(answering) , END_CALL cmd , Cancel
+ if (evt instanceof MakeCallCommand) {
+ // answer call
+ mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
+ mLocalProfile,
+ ((MakeCallCommand) evt).getSessionDescription(),
+ mServerTransaction);
+ mState = SipSessionState.INCOMING_CALL_ANSWERING;
+ return true;
+ } else if (END_CALL == evt) {
+ mSipHelper.sendInviteBusyHere(mInviteReceived,
+ mServerTransaction);
+ endCallNormally();
+ return true;
+ } else if (isRequestEvent(Request.CANCEL, evt)) {
+ RequestEvent event = (RequestEvent) evt;
+ mSipHelper.sendResponse(event, Response.OK);
+ mSipHelper.sendInviteRequestTerminated(
+ mInviteReceived.getRequest(), mServerTransaction);
+ endCallNormally();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean incomingCallToInCall(EventObject evt)
+ throws SipException {
+ // expect ACK, CANCEL request
+ if (isRequestEvent(Request.ACK, evt)) {
+ establishCall();
+ return true;
+ } else if (isRequestEvent(Request.CANCEL, evt)) {
+ // http://tools.ietf.org/html/rfc3261#section-9.2
+ // Final response has been sent; do nothing here.
+ return true;
+ }
+ return false;
+ }
+
+ private boolean outgoingCall(EventObject evt) throws SipException {
+ if (expectResponse(Request.INVITE, evt)) {
+ ResponseEvent event = (ResponseEvent) evt;
+ Response response = event.getResponse();
+
+ int statusCode = response.getStatusCode();
+ switch (statusCode) {
+ case Response.RINGING:
+ if (mState == SipSessionState.OUTGOING_CALL) {
+ mState = SipSessionState.OUTGOING_CALL_RING_BACK;
+ mProxy.onRingingBack(this);
+ }
+ return true;
+ case Response.OK:
+ mSipHelper.sendInviteAck(event, mDialog);
+ mPeerSessionDescription = response.getRawContent();
+ establishCall();
+ return true;
+ case Response.PROXY_AUTHENTICATION_REQUIRED:
+ mClientTransaction = mSipHelper.handleChallenge(
+ (ResponseEvent) evt, getAccountManager());
+ mDialog = mClientTransaction.getDialog();
+ addSipSession(this);
+ return true;
+ case Response.BUSY_HERE:
+ reset();
+ mProxy.onCallBusy(this);
+ return true;
+ case Response.REQUEST_PENDING:
+ // TODO:
+ // rfc3261#section-14.1; re-schedule invite
+ return true;
+ default:
+ if (statusCode >= 400) {
+ // error: an ack is sent automatically by the stack
+ onError(createCallbackException(response));
+ return true;
+ } else if (statusCode >= 300) {
+ // TODO: handle 3xx (redirect)
+ } else {
+ return true;
+ }
+ }
+ return false;
+ } else if (END_CALL == evt) {
+ // RFC says that UA should not send out cancel when no
+ // response comes back yet. We are cheating for not checking
+ // response.
+ mSipHelper.sendCancel(mClientTransaction);
+ mState = SipSessionState.OUTGOING_CALL_CANCELING;
+ return true;
+ }
+ return false;
+ }
+
+ private boolean outgoingCallToReady(EventObject evt)
+ throws SipException {
+ if (evt instanceof ResponseEvent) {
+ ResponseEvent event = (ResponseEvent) evt;
+ Response response = event.getResponse();
+ int statusCode = response.getStatusCode();
+ if (expectResponse(Request.CANCEL, evt)) {
+ if (statusCode == Response.OK) {
+ // do nothing; wait for REQUEST_TERMINATED
+ return true;
+ }
+ } else if (expectResponse(Request.INVITE, evt)) {
+ if (statusCode == Response.OK) {
+ outgoingCall(evt); // abort Cancel
+ return true;
+ }
+ } else {
+ return false;
+ }
+
+ if (statusCode >= 400) {
+ onError(createCallbackException(response));
+ return true;
+ }
+ } else if (evt instanceof TransactionTerminatedEvent) {
+ // rfc3261#section-14.1:
+ // if re-invite gets timed out, terminate the dialog; but
+ // re-invite is not reliable, just let it go and pretend
+ // nothing happened.
+ onError(new SipException("timed out"));
+ }
+ return false;
+ }
+
+ private boolean inCall(EventObject evt) throws SipException {
+ // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
+ // OK retransmission is handled in SipStack
+ if (END_CALL == evt) {
+ // rfc3261#section-15.1.1
+ mSipHelper.sendBye(mDialog);
+ endCallNormally();
+ return true;
+ } else if (isRequestEvent(Request.INVITE, evt)) {
+ // got Re-INVITE
+ RequestEvent event = mInviteReceived = (RequestEvent) evt;
+ mState = SipSessionState.INCOMING_CALL;
+ mPeerSessionDescription = event.getRequest().getRawContent();
+ mServerTransaction = null;
+ mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
+ return true;
+ } else if (isRequestEvent(Request.BYE, evt)) {
+ mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
+ endCallNormally();
+ return true;
+ } else if (evt instanceof MakeCallCommand) {
+ // to change call
+ mClientTransaction = mSipHelper.sendReinvite(mDialog,
+ ((MakeCallCommand) evt).getSessionDescription());
+ mState = SipSessionState.OUTGOING_CALL;
+ return true;
+ }
+ return false;
+ }
+
+ private Exception createCallbackException(Response response) {
+ return new SipException(String.format("Response: %s (%d)",
+ response.getReasonPhrase(), response.getStatusCode()));
+ }
+
+ private void establishCall() {
+ mState = SipSessionState.IN_CALL;
+ mInCall = true;
+ mProxy.onCallEstablished(this, mPeerSessionDescription);
+ }
+
+ private void fallbackToPreviousInCall(Throwable exception) {
+ mState = SipSessionState.IN_CALL;
+ mProxy.onCallChangeFailed(this, exception.getClass().getName(),
+ exception.getMessage());
+ }
+
+ private void endCallNormally() {
+ reset();
+ mProxy.onCallEnded(this);
+ }
+
+ private void endCallOnError(Throwable exception) {
+ reset();
+ mProxy.onError(this, exception.getClass().getName(),
+ exception.getMessage());
+ }
+
+ private void onError(Throwable exception) {
+ if (mInCall) {
+ fallbackToPreviousInCall(exception);
+ } else {
+ endCallOnError(exception);
+ }
+ }
+
+ private void onRegistrationDone(int duration) {
+ mProxy.onRegistrationDone(this, duration);
+ }
+
+ private void onRegistrationFailed(Throwable exception) {
+ mProxy.onRegistrationFailed(this, exception.getClass().getName(),
+ exception.getMessage());
+ }
+ }
+
+ /**
+ * @return true if the event is a request event matching the specified
+ * method; false otherwise
+ */
+ private static boolean isRequestEvent(String method, EventObject event) {
+ try {
+ if (event instanceof RequestEvent) {
+ RequestEvent requestEvent = (RequestEvent) event;
+ return method.equals(requestEvent.getRequest().getMethod());
+ }
+ } catch (Throwable e) {
+ }
+ return false;
+ }
+
+ private static String getCseqMethod(Message message) {
+ return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
+ }
+
+ /**
+ * @return true if the event is a response event and the CSeqHeader method
+ * match the given arguments; false otherwise
+ */
+ private static boolean expectResponse(
+ String expectedMethod, EventObject evt) {
+ if (evt instanceof ResponseEvent) {
+ ResponseEvent event = (ResponseEvent) evt;
+ Response response = event.getResponse();
+ return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
+ }
+ return false;
+ }
+
+ /**
+ * @return true if the event is a response event and the response code and
+ * CSeqHeader method match the given arguments; false otherwise
+ */
+ private static boolean expectResponse(
+ int responseCode, String expectedMethod, EventObject evt) {
+ if (evt instanceof ResponseEvent) {
+ ResponseEvent event = (ResponseEvent) evt;
+ Response response = event.getResponse();
+ if (response.getStatusCode() == responseCode) {
+ return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
+ }
+ }
+ return false;
+ }
+
+ private static SipProfile createPeerProfile(Request request)
+ throws SipException {
+ try {
+ FromHeader fromHeader =
+ (FromHeader) request.getHeader(FromHeader.NAME);
+ Address address = fromHeader.getAddress();
+ SipURI uri = (SipURI) address.getURI();
+ String username = uri.getUser();
+ if (username == null) username = ANONYMOUS;
+ return new SipProfile.Builder(username, uri.getHost())
+ .setPort(uri.getPort())
+ .setDisplayName(address.getDisplayName())
+ .build();
+ } catch (InvalidArgumentException e) {
+ throw new SipException("createPeerProfile()", e);
+ } catch (ParseException e) {
+ throw new SipException("createPeerProfile()", e);
+ }
+ }
+
+ private static String log(EventObject evt) {
+ if (evt instanceof RequestEvent) {
+ return ((RequestEvent) evt).getRequest().toString();
+ } else if (evt instanceof ResponseEvent) {
+ return ((ResponseEvent) evt).getResponse().toString();
+ } else {
+ return evt.toString();
+ }
+ }
+
+ private class OptionsCommand extends EventObject {
+ public OptionsCommand() {
+ super(SipSessionGroup.this);
+ }
+ }
+
+ private class RegisterCommand extends EventObject {
+ private int mDuration;
+
+ public RegisterCommand(int duration) {
+ super(SipSessionGroup.this);
+ mDuration = duration;
+ }
+
+ public int getDuration() {
+ return mDuration;
+ }
+ }
+
+ private class MakeCallCommand extends EventObject {
+ private SessionDescription mSessionDescription;
+
+ public MakeCallCommand(SipProfile peerProfile,
+ SessionDescription sessionDescription) {
+ super(peerProfile);
+ mSessionDescription = sessionDescription;
+ }
+
+ public SipProfile getPeerProfile() {
+ return (SipProfile) getSource();
+ }
+
+ public SessionDescription getSessionDescription() {
+ return mSessionDescription;
+ }
+ }
+
+}
diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/services/java/com/android/server/sip/SipSessionListenerProxy.java
new file mode 100644
index 0000000..fd49fd8
--- /dev/null
+++ b/services/java/com/android/server/sip/SipSessionListenerProxy.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sip;
+
+import android.net.sip.ISipSession;
+import android.net.sip.ISipSessionListener;
+import android.net.sip.SipProfile;
+import android.util.Log;
+
+/** Class to help safely run a callback in a different thread. */
+class SipSessionListenerProxy extends ISipSessionListener.Stub {
+ private static final String TAG = "SipSession";
+
+ private ISipSessionListener mListener;
+
+ public void setListener(ISipSessionListener listener) {
+ mListener = listener;
+ }
+
+ public ISipSessionListener getListener() {
+ return mListener;
+ }
+
+ private void proxy(Runnable runnable) {
+ // One thread for each calling back.
+ // Note: Guarantee ordering if the issue becomes important. Currently,
+ // the chance of handling two callback events at a time is none.
+ new Thread(runnable).start();
+ }
+
+ public void onCalling(final ISipSession session) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onCalling(session);
+ } catch (Throwable t) {
+ Log.w(TAG, "onCalling()", t);
+ }
+ }
+ });
+ }
+
+ public void onRinging(final ISipSession session, final SipProfile caller,
+ final byte[] sessionDescription) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onRinging(session, caller, sessionDescription);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRinging()", t);
+ }
+ }
+ });
+ }
+
+ public void onRingingBack(final ISipSession session) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onRingingBack(session);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRingingBack()", t);
+ }
+ }
+ });
+ }
+
+ public void onCallEstablished(final ISipSession session,
+ final byte[] sessionDescription) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onCallEstablished(session, sessionDescription);
+ } catch (Throwable t) {
+ Log.w(TAG, "onCallEstablished()", t);
+ }
+ }
+ });
+ }
+
+ public void onCallEnded(final ISipSession session) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onCallEnded(session);
+ } catch (Throwable t) {
+ Log.w(TAG, "onCallEnded()", t);
+ }
+ }
+ });
+ }
+
+ public void onCallBusy(final ISipSession session) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onCallBusy(session);
+ } catch (Throwable t) {
+ Log.w(TAG, "onCallBusy()", t);
+ }
+ }
+ });
+ }
+
+ public void onCallChangeFailed(final ISipSession session,
+ final String className, final String message) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onCallChangeFailed(session, className, message);
+ } catch (Throwable t) {
+ Log.w(TAG, "onCallChangeFailed()", t);
+ }
+ }
+ });
+ }
+
+ public void onError(final ISipSession session, final String className,
+ final String message) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onError(session, className, message);
+ } catch (Throwable t) {
+ Log.w(TAG, "onError()", t);
+ }
+ }
+ });
+ }
+
+ public void onRegistering(final ISipSession session) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onRegistering(session);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRegistering()", t);
+ }
+ }
+ });
+ }
+
+ public void onRegistrationDone(final ISipSession session,
+ final int duration) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onRegistrationDone(session, duration);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRegistrationDone()", t);
+ }
+ }
+ });
+ }
+
+ public void onRegistrationFailed(final ISipSession session,
+ final String className, final String message) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onRegistrationFailed(session, className, message);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRegistrationFailed()", t);
+ }
+ }
+ });
+ }
+
+ public void onRegistrationTimeout(final ISipSession session) {
+ if (mListener == null) return;
+ proxy(new Runnable() {
+ public void run() {
+ try {
+ mListener.onRegistrationTimeout(session);
+ } catch (Throwable t) {
+ Log.w(TAG, "onRegistrationTimeout()", t);
+ }
+ }
+ });
+ }
+}
diff --git a/services/java/com/android/server/status/AnimatedImageView.java b/services/java/com/android/server/status/AnimatedImageView.java
deleted file mode 100644
index 97df065..0000000
--- a/services/java/com/android/server/status/AnimatedImageView.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.RemoteViews.RemoteView;
-
-@RemoteView
-public class AnimatedImageView extends ImageView {
- AnimationDrawable mAnim;
- boolean mAttached;
-
- public AnimatedImageView(Context context) {
- super(context);
- }
-
- public AnimatedImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- private void updateAnim() {
- Drawable drawable = getDrawable();
- if (mAttached && mAnim != null) {
- mAnim.stop();
- }
- if (drawable instanceof AnimationDrawable) {
- mAnim = (AnimationDrawable)drawable;
- if (mAttached) {
- mAnim.start();
- }
- } else {
- mAnim = null;
- }
- }
-
- @Override
- public void setImageDrawable(Drawable drawable) {
- super.setImageDrawable(drawable);
- updateAnim();
- }
-
- @Override
- @android.view.RemotableViewMethod
- public void setImageResource(int resid) {
- super.setImageResource(resid);
- updateAnim();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mAnim != null) {
- mAnim.start();
- }
- mAttached = true;
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAnim != null) {
- mAnim.stop();
- }
- mAttached = false;
- }
-}
-
diff --git a/services/java/com/android/server/status/CloseDragHandle.java b/services/java/com/android/server/status/CloseDragHandle.java
deleted file mode 100644
index ad1ac4d..0000000
--- a/services/java/com/android/server/status/CloseDragHandle.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.LinearLayout;
-
-
-public class CloseDragHandle extends LinearLayout {
- StatusBarService mService;
-
- public CloseDragHandle(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /**
- * Ensure that, if there is no target under us to receive the touch,
- * that we process it ourself. This makes sure that onInterceptTouchEvent()
- * is always called for the entire gesture.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() != MotionEvent.ACTION_DOWN) {
- mService.interceptTouchEvent(event);
- }
- return true;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return mService.interceptTouchEvent(event)
- ? true : super.onInterceptTouchEvent(event);
- }
-}
-
diff --git a/services/java/com/android/server/status/DateView.java b/services/java/com/android/server/status/DateView.java
deleted file mode 100644
index c04fb45..0000000
--- a/services/java/com/android/server/status/DateView.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.widget.TextView;
-import android.view.MotionEvent;
-
-import java.text.DateFormat;
-import java.util.Date;
-
-public final class DateView extends TextView {
- private static final String TAG = "DateView";
-
- private boolean mUpdating = false;
-
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_TIME_TICK)
- || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- updateClock();
- }
- }
- };
-
- public DateView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- setUpdates(false);
- }
-
- @Override
- protected int getSuggestedMinimumWidth() {
- // makes the large background bitmap not force us to full width
- return 0;
- }
-
- private final void updateClock() {
- Date now = new Date();
- setText(DateFormat.getDateInstance(DateFormat.LONG).format(now));
- }
-
- void setUpdates(boolean update) {
- if (update != mUpdating) {
- mUpdating = update;
- if (update) {
- // Register for Intent broadcasts for the clock and battery
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- mContext.registerReceiver(mIntentReceiver, filter, null, null);
- updateClock();
- } else {
- mContext.unregisterReceiver(mIntentReceiver);
- }
- }
- }
-}
-
diff --git a/services/java/com/android/server/status/ExpandedView.java b/services/java/com/android/server/status/ExpandedView.java
deleted file mode 100644
index cb37f90..0000000
--- a/services/java/com/android/server/status/ExpandedView.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-import android.util.Slog;
-
-
-public class ExpandedView extends LinearLayout {
- StatusBarService mService;
- int mPrevHeight = -1;
-
- public ExpandedView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- }
-
- /** We want to shrink down to 0, and ignore the background. */
- @Override
- public int getSuggestedMinimumHeight() {
- return 0;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- int height = bottom - top;
- if (height != mPrevHeight) {
- //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight + " new=" + height);
- mPrevHeight = height;
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
- }
- }
-}
diff --git a/services/java/com/android/server/status/FixedSizeDrawable.java b/services/java/com/android/server/status/FixedSizeDrawable.java
deleted file mode 100644
index dbfcb2c..0000000
--- a/services/java/com/android/server/status/FixedSizeDrawable.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.graphics.drawable.Drawable;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.util.Slog;
-
-class FixedSizeDrawable extends Drawable {
- Drawable mDrawable;
- int mLeft;
- int mTop;
- int mRight;
- int mBottom;
-
- FixedSizeDrawable(Drawable that) {
- mDrawable = that;
- }
-
- public void setFixedBounds(int l, int t, int r, int b) {
- mLeft = l;
- mTop = t;
- mRight = r;
- mBottom = b;
- }
-
- public void setBounds(Rect bounds) {
- mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
- }
-
- public void setBounds(int l, int t, int r, int b) {
- mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
- }
-
- public void draw(Canvas canvas) {
- mDrawable.draw(canvas);
- }
-
- public int getOpacity() {
- return mDrawable.getOpacity();
- }
-
- public void setAlpha(int alpha) {
- mDrawable.setAlpha(alpha);
- }
-
- public void setColorFilter(ColorFilter cf) {
- mDrawable.setColorFilter(cf);
- }
-}
diff --git a/services/java/com/android/server/status/IconData.java b/services/java/com/android/server/status/IconData.java
deleted file mode 100644
index fd226f9..0000000
--- a/services/java/com/android/server/status/IconData.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.util.Slog;
-
-public class IconData {
- /**
- * Indicates ths item represents a piece of text.
- */
- public static final int TEXT = 1;
-
- /**
- * Indicates ths item represents an icon.
- */
- public static final int ICON = 2;
-
- /**
- * The type of this item. One of TEXT, ICON, or LEVEL_ICON.
- */
- public int type;
-
- /**
- * The slot that this icon will be in if it is not a notification
- */
- public String slot;
-
- /**
- * The package containting the icon to draw for this item. Valid if this is
- * an ICON type.
- */
- public String iconPackage;
-
- /**
- * The icon to draw for this item. Valid if this is an ICON type.
- */
- public int iconId;
-
- /**
- * The level associated with the icon. Valid if this is a LEVEL_ICON type.
- */
- public int iconLevel;
-
- /**
- * The "count" number.
- */
- public int number;
-
- /**
- * The text associated with the icon. Valid if this is a TEXT type.
- */
- public CharSequence text;
-
- private IconData() {
- }
-
- public static IconData makeIcon(String slot,
- String iconPackage, int iconId, int iconLevel, int number) {
- IconData data = new IconData();
- data.type = ICON;
- data.slot = slot;
- data.iconPackage = iconPackage;
- data.iconId = iconId;
- data.iconLevel = iconLevel;
- data.number = number;
- return data;
- }
-
- public static IconData makeText(String slot, CharSequence text) {
- IconData data = new IconData();
- data.type = TEXT;
- data.slot = slot;
- data.text = text;
- return data;
- }
-
- public void copyFrom(IconData that) {
- this.type = that.type;
- this.slot = that.slot;
- this.iconPackage = that.iconPackage;
- this.iconId = that.iconId;
- this.iconLevel = that.iconLevel;
- this.number = that.number;
- this.text = that.text; // should we clone this?
- }
-
- public IconData clone() {
- IconData that = new IconData();
- that.copyFrom(this);
- return that;
- }
-
- public String toString() {
- if (this.type == TEXT) {
- return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
- + " text='" + this.text + "')";
- }
- else if (this.type == ICON) {
- return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
- + " package=" + this.iconPackage
- + " iconId=" + Integer.toHexString(this.iconId)
- + " iconLevel=" + this.iconLevel + ")";
- }
- else {
- return "IconData(type=" + type + ")";
- }
- }
-}
diff --git a/services/java/com/android/server/status/IconMerger.java b/services/java/com/android/server/status/IconMerger.java
deleted file mode 100644
index aa702ae..0000000
--- a/services/java/com/android/server/status/IconMerger.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-
-
-public class IconMerger extends LinearLayout {
- StatusBarService service;
- StatusBarIcon moreIcon;
-
- public IconMerger(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
-
- final int maxWidth = r - l;
- final int N = getChildCount();
- int i;
-
- // get the rightmost one, and see if we even need to do anything
- int fitRight = -1;
- for (i=N-1; i>=0; i--) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- fitRight = child.getRight();
- break;
- }
- }
-
- // find the first visible one that isn't the more icon
- View moreView = null;
- int fitLeft = -1;
- int startIndex = -1;
- for (i=0; i<N; i++) {
- final View child = getChildAt(i);
- if (com.android.internal.R.drawable.stat_notify_more == child.getId()) {
- moreView = child;
- startIndex = i+1;
- }
- else if (child.getVisibility() != GONE) {
- fitLeft = child.getLeft();
- break;
- }
- }
-
- if (moreView == null || startIndex < 0) {
- throw new RuntimeException("Status Bar / IconMerger moreView == null");
- }
-
- // if it fits without the more icon, then hide the more icon and update fitLeft
- // so everything gets pushed left
- int adjust = 0;
- if (fitRight - fitLeft <= maxWidth) {
- adjust = fitLeft - moreView.getLeft();
- fitLeft -= adjust;
- fitRight -= adjust;
- moreView.layout(0, moreView.getTop(), 0, moreView.getBottom());
- }
- int extra = fitRight - r;
- int shift = -1;
-
- int breakingPoint = fitLeft + extra + adjust;
- int number = 0;
- for (i=startIndex; i<N; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- int childLeft = child.getLeft();
- int childRight = child.getRight();
- if (childLeft < breakingPoint) {
- // hide this one
- child.layout(0, child.getTop(), 0, child.getBottom());
- int n = this.service.getIconNumberForView(child);
- if (n == 0) {
- number += 1;
- } else if (n > 0) {
- number += n;
- }
- } else {
- // decide how much to shift by
- if (shift < 0) {
- shift = childLeft - fitLeft;
- }
- // shift this left by shift
- child.layout(childLeft-shift, child.getTop(),
- childRight-shift, child.getBottom());
- }
- }
- }
-
- // BUG: Updating the text during the layout here doesn't seem to cause
- // the view to be redrawn fully. The text view gets resized correctly, but the
- // text contents aren't drawn properly. To work around this, we post a message
- // and provide the value later. We're the only one changing this value show it
- // should be ordered correctly.
- if (false) {
- this.moreIcon.update(number);
- } else {
- mBugWorkaroundNumber = number;
- mBugWorkaroundHandler.post(mBugWorkaroundRunnable);
- }
- }
-
- private int mBugWorkaroundNumber;
- private Handler mBugWorkaroundHandler = new Handler();
- private Runnable mBugWorkaroundRunnable = new Runnable() {
- public void run() {
- IconMerger.this.moreIcon.update(mBugWorkaroundNumber);
- IconMerger.this.moreIcon.view.invalidate();
- }
- };
-}
diff --git a/services/java/com/android/server/status/LatestItemView.java b/services/java/com/android/server/status/LatestItemView.java
deleted file mode 100644
index fe8d164..0000000
--- a/services/java/com/android/server/status/LatestItemView.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-public class LatestItemView extends FrameLayout {
-
- public LatestItemView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public boolean dispatchTouchEvent(MotionEvent ev) {
- return onTouchEvent(ev);
- }
-}
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
deleted file mode 100644
index 71f01ca..0000000
--- a/services/java/com/android/server/status/NotificationData.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.app.PendingIntent;
-import android.widget.RemoteViews;
-
-public class NotificationData {
- public String pkg;
- public String tag;
- public int id;
- public CharSequence tickerText;
-
- public long when;
- public boolean ongoingEvent;
- public boolean clearable;
-
- public RemoteViews contentView;
- public PendingIntent contentIntent;
-
- public PendingIntent deleteIntent;
-
- public String toString() {
- return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText
- + " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
- + " deleteIntent=" + deleteIntent
- + " clearable=" + clearable
- + " contentView=" + contentView + " when=" + when + ")";
- }
-}
diff --git a/services/java/com/android/server/status/NotificationLinearLayout.java b/services/java/com/android/server/status/NotificationLinearLayout.java
deleted file mode 100644
index 2fdf956..0000000
--- a/services/java/com/android/server/status/NotificationLinearLayout.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-
-
-public class NotificationLinearLayout extends LinearLayout {
- public NotificationLinearLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-}
-
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
deleted file mode 100644
index 1bb56a7..0000000
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.os.IBinder;
-import android.util.Slog;
-import android.view.View;
-import java.util.ArrayList;
-
-class NotificationViewList {
- private ArrayList<StatusBarNotification> mOngoing = new ArrayList();
- private ArrayList<StatusBarNotification> mLatest = new ArrayList();
-
- NotificationViewList() {
- }
-
- private static final int indexInList(ArrayList<StatusBarNotification> list, NotificationData n){
- final int N = list.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification that = list.get(i);
- if (that.data == n) {
- return i;
- }
- }
- return -1;
- }
-
- int getIconIndex(NotificationData n) {
- final int ongoingSize = mOngoing.size();
- final int latestSize = mLatest.size();
- if (n.ongoingEvent) {
- int index = indexInList(mOngoing, n);
- if (index >= 0) {
- return latestSize + index + 1;
- } else {
- return -1;
- }
- } else {
- return indexInList(mLatest, n) + 1;
- }
- }
-
- void remove(StatusBarNotification notification) {
- NotificationData n = notification.data;
- int index;
- index = indexInList(mOngoing, n);
- if (index >= 0) {
- mOngoing.remove(index);
- return;
- }
- index = indexInList(mLatest, n);
- if (index >= 0) {
- mLatest.remove(index);
- return;
- }
- }
-
- ArrayList<StatusBarNotification> notificationsForPackage(String packageName) {
- ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>();
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- if (matchPackage(mOngoing.get(i), packageName)) {
- list.add(mOngoing.get(i));
- }
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- if (matchPackage(mLatest.get(i), packageName)) {
- list.add(mLatest.get(i));
- }
- }
- return list;
- }
-
- private final boolean matchPackage(StatusBarNotification snb, String packageName) {
- if (snb.data.contentIntent != null) {
- if (snb.data.contentIntent.getTargetPackage().equals(packageName)) {
- return true;
- }
- } else if (snb.data.pkg != null && snb.data.pkg.equals(packageName)) {
- return true;
- }
- return false;
- }
-
- private static final int indexForKey(ArrayList<StatusBarNotification> list, IBinder key) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- if (list.get(i).key == key) {
- return i;
- }
- }
- return -1;
- }
-
- StatusBarNotification get(IBinder key) {
- int index;
- index = indexForKey(mOngoing, key);
- if (index >= 0) {
- return mOngoing.get(index);
- }
- index = indexForKey(mLatest, key);
- if (index >= 0) {
- return mLatest.get(index);
- }
- return null;
- }
-
- // gets the index of the notification's view in its expanded parent view
- int getExpandedIndex(StatusBarNotification notification) {
- ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
- final IBinder key = notification.key;
- int index = 0;
- // (the view order is backwards from this list order)
- for (int i=list.size()-1; i>=0; i--) {
- StatusBarNotification item = list.get(i);
- if (item.key == key) {
- return index;
- }
- if (item.view != null) {
- index++;
- }
- }
- Slog.e(StatusBarService.TAG, "Couldn't find notification in NotificationViewList.");
- Slog.e(StatusBarService.TAG, "notification=" + notification);
- dump(notification);
- return 0;
- }
-
- void clearViews() {
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- mOngoing.get(i).view = null;
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- mLatest.get(i).view = null;
- }
- }
-
- int ongoingCount() {
- return mOngoing.size();
- }
-
- int latestCount() {
- return mLatest.size();
- }
-
- StatusBarNotification getOngoing(int index) {
- return mOngoing.get(index);
- }
-
- StatusBarNotification getLatest(int index) {
- return mLatest.get(index);
- }
-
- int size() {
- return mOngoing.size() + mLatest.size();
- }
-
- void add(StatusBarNotification notification) {
- if (StatusBarService.SPEW) {
- Slog.d(StatusBarService.TAG, "before add NotificationViewList"
- + " notification.data.ongoingEvent=" + notification.data.ongoingEvent);
- dump(notification);
- }
-
- ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
- long when = notification.data.when;
- final int N = list.size();
- int index = N;
- for (int i=0; i<N; i++) {
- StatusBarNotification that = list.get(i);
- if (that.data.when > when) {
- index = i;
- break;
- }
- }
- list.add(index, notification);
-
- if (StatusBarService.SPEW) {
- Slog.d(StatusBarService.TAG, "after add NotificationViewList index=" + index);
- dump(notification);
- }
- }
-
- void dump(StatusBarNotification notification) {
- if (StatusBarService.SPEW) {
- boolean showTime = false;
- String s = "";
- for (int i=0; i<mOngoing.size(); i++) {
- StatusBarNotification that = mOngoing.get(i);
- if (that.key == notification.key) {
- s += "[";
- }
- if (showTime) {
- s += that.data.when;
- } else {
- s += that.data.pkg + "/" + that.data.id + "/" + that.view;
- }
- if (that.key == notification.key) {
- s += "]";
- }
- s += " ";
- }
- Slog.d(StatusBarService.TAG, "NotificationViewList ongoing: " + s);
-
- s = "";
- for (int i=0; i<mLatest.size(); i++) {
- StatusBarNotification that = mLatest.get(i);
- if (that.key == notification.key) {
- s += "[";
- }
- if (showTime) {
- s += that.data.when;
- } else {
- s += that.data.pkg + "/" + that.data.id + "/" + that.view;
- }
- if (that.key == notification.key) {
- s += "]";
- }
- s += " ";
- }
- Slog.d(StatusBarService.TAG, "NotificationViewList latest: " + s);
- }
- }
-
- StatusBarNotification get(View view) {
- int N = mOngoing.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification notification = mOngoing.get(i);
- View v = notification.view;
- if (v == view) {
- return notification;
- }
- }
- N = mLatest.size();
- for (int i=0; i<N; i++) {
- StatusBarNotification notification = mLatest.get(i);
- View v = notification.view;
- if (v == view) {
- return notification;
- }
- }
- return null;
- }
-
- void update(StatusBarNotification notification) {
- remove(notification);
- add(notification);
- }
-
- boolean hasClearableItems() {
- int N = mLatest.size();
- for (int i=0; i<N; i++) {
- if (mLatest.get(i).data.clearable) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java
deleted file mode 100644
index 6f8b8a8..0000000
--- a/services/java/com/android/server/status/StatusBarIcon.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-class StatusBarIcon {
- // TODO: get this from a resource
- private static final int ICON_GAP = 8;
- private static final int ICON_WIDTH = 25;
- private static final int ICON_HEIGHT = 25;
-
- public View view;
-
- IconData mData;
-
- private TextView mTextView;
- private AnimatedImageView mImageView;
- private TextView mNumberView;
-
- public StatusBarIcon(Context context, IconData data, ViewGroup parent) {
- mData = data.clone();
-
- switch (data.type) {
- case IconData.TEXT: {
- TextView t;
- t = new TextView(context);
- mTextView = t;
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.MATCH_PARENT);
- t.setTextSize(16);
- t.setTextColor(0xff000000);
- t.setTypeface(Typeface.DEFAULT_BOLD);
- t.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
- t.setPadding(6, 0, 0, 0);
- t.setLayoutParams(layoutParams);
- t.setText(data.text);
- this.view = t;
- break;
- }
-
- case IconData.ICON: {
- // container
- LayoutInflater inflater = (LayoutInflater)context.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View v = inflater.inflate(com.android.internal.R.layout.status_bar_icon, parent, false);
- this.view = v;
-
- // icon
- AnimatedImageView im = (AnimatedImageView)v.findViewById(com.android.internal.R.id.image);
- im.setImageDrawable(getIcon(context, data));
- im.setImageLevel(data.iconLevel);
- mImageView = im;
-
- // number
- TextView nv = (TextView)v.findViewById(com.android.internal.R.id.number);
- mNumberView = nv;
- if (data.number > 0) {
- nv.setText("" + data.number);
- nv.setVisibility(View.VISIBLE);
- } else {
- nv.setVisibility(View.GONE);
- }
- break;
- }
- }
- }
-
- public void update(Context context, IconData data) throws StatusBarException {
- if (mData.type != data.type) {
- throw new StatusBarException("status bar entry type can't change");
- }
- switch (data.type) {
- case IconData.TEXT:
- if (!TextUtils.equals(mData.text, data.text)) {
- TextView tv = mTextView;
- tv.setText(data.text);
- }
- break;
- case IconData.ICON:
- if (((mData.iconPackage != null && data.iconPackage != null)
- && !mData.iconPackage.equals(data.iconPackage))
- || mData.iconId != data.iconId
- || mData.iconLevel != data.iconLevel) {
- ImageView im = mImageView;
- im.setImageDrawable(getIcon(context, data));
- im.setImageLevel(data.iconLevel);
- }
- if (mData.number != data.number) {
- TextView nv = mNumberView;
- if (data.number > 0) {
- nv.setText("" + data.number);
- } else {
- nv.setText("");
- }
- }
- break;
- }
- mData.copyFrom(data);
- }
-
- public void update(int number) {
- if (mData.number != number) {
- TextView nv = mNumberView;
- if (number > 0) {
- nv.setText("" + number);
- } else {
- nv.setText("");
- }
- }
- mData.number = number;
- }
-
-
- /**
- * Returns the right icon to use for this item, respecting the iconId and
- * iconPackage (if set)
- *
- * @param context Context to use to get resources if iconPackage is not set
- * @return Drawable for this item, or null if the package or item could not
- * be found
- */
- static Drawable getIcon(Context context, IconData data) {
-
- Resources r = null;
-
- if (data.iconPackage != null) {
- try {
- r = context.getPackageManager().getResourcesForApplication(data.iconPackage);
- } catch (PackageManager.NameNotFoundException ex) {
- Slog.e(StatusBarService.TAG, "Icon package not found: " + data.iconPackage, ex);
- return null;
- }
- } else {
- r = context.getResources();
- }
-
- if (data.iconId == 0) {
- Slog.w(StatusBarService.TAG, "No icon ID for slot " + data.slot);
- return null;
- }
-
- try {
- return r.getDrawable(data.iconId);
- } catch (RuntimeException e) {
- Slog.w(StatusBarService.TAG, "Icon not found in "
- + (data.iconPackage != null ? data.iconId : "<system>")
- + ": " + Integer.toHexString(data.iconId));
- }
-
- return null;
- }
-
- int getNumber() {
- return mData.number;
- }
-}
-
diff --git a/services/java/com/android/server/status/StatusBarNotification.java b/services/java/com/android/server/status/StatusBarNotification.java
deleted file mode 100644
index e5773f7..0000000
--- a/services/java/com/android/server/status/StatusBarNotification.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.os.IBinder;
-import android.view.View;
-
-class StatusBarNotification {
- IBinder key;
- NotificationData data;
- View view;
- View contentView;
-}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
deleted file mode 100644
index 3b0c436..0000000
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ /dev/null
@@ -1,1390 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.app.AlertDialog;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothPbap;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.NetworkInfo;
-import android.net.Uri;
-import android.net.wifi.WifiManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.storage.StorageManager;
-import android.provider.Settings;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
-import android.text.format.DateFormat;
-import android.text.style.RelativeSizeSpan;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.util.Slog;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.location.GpsLocationProvider;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.EriInfo;
-import com.android.internal.telephony.cdma.TtyIntent;
-import com.android.server.am.BatteryStatsService;
-
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.TimeZone;
-
-/**
- * This class contains all of the policy about which icons are installed in the status
- * bar at boot time. In reality, it should go into the android.policy package, but
- * putting it here is the first step from extracting it.
- */
-public class StatusBarPolicy {
- private static final String TAG = "StatusBarPolicy";
-
- private static StatusBarPolicy sInstance;
-
- // message codes for the handler
- private static final int EVENT_BATTERY_CLOSE = 4;
-
- private final Context mContext;
- private final StatusBarService mService;
- private final Handler mHandler = new StatusBarHandler();
- private final IBatteryStats mBatteryStats;
-
- // clock
- private Calendar mCalendar;
- private String mClockFormatString;
- private SimpleDateFormat mClockFormat;
- private IBinder mClockIcon;
- private IconData mClockData;
-
- // storage
- private StorageManager mStorageManager;
-
- // battery
- private IBinder mBatteryIcon;
- private IconData mBatteryData;
- private boolean mBatteryFirst = true;
- private boolean mBatteryPlugged;
- private int mBatteryLevel;
- private AlertDialog mLowBatteryDialog;
- private TextView mBatteryLevelTextView;
- private View mBatteryView;
- private int mBatteryViewSequence;
- private boolean mBatteryShowLowOnEndCall = false;
- private static final boolean SHOW_LOW_BATTERY_WARNING = true;
- private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
-
- // phone
- private TelephonyManager mPhone;
- private IBinder mPhoneIcon;
-
- //***** Signal strength icons
- private IconData mPhoneData;
- //GSM/UMTS
- private static final int[] sSignalImages = new int[] {
- com.android.internal.R.drawable.stat_sys_signal_0,
- com.android.internal.R.drawable.stat_sys_signal_1,
- com.android.internal.R.drawable.stat_sys_signal_2,
- com.android.internal.R.drawable.stat_sys_signal_3,
- com.android.internal.R.drawable.stat_sys_signal_4
- };
- private static final int[] sSignalImages_r = new int[] {
- com.android.internal.R.drawable.stat_sys_r_signal_0,
- com.android.internal.R.drawable.stat_sys_r_signal_1,
- com.android.internal.R.drawable.stat_sys_r_signal_2,
- com.android.internal.R.drawable.stat_sys_r_signal_3,
- com.android.internal.R.drawable.stat_sys_r_signal_4
- };
- private static final int[] sRoamingIndicatorImages_cdma = new int[] {
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
- // 1 is Standard Roaming Indicator OFF
- // TODO T: image never used, remove and put 0 instead?
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 2 is Standard Roaming Indicator FLASHING
- // TODO T: image never used, remove and put 0 instead?
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 3-12 Standard ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //3
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 13-63 Reserved for Standard ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //13
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
-
- // 64-127 Reserved for Non Standard (Operator Specific) ERI
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //64
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
- com.android.internal.R.drawable.stat_sys_roaming_cdma_0 //83
-
- // 128-255 Reserved
- };
-
- //***** Data connection icons
- private int[] mDataIconList = sDataNetType_g;
- //GSM/UMTS
- private static final int[] sDataNetType_g = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_g,
- com.android.internal.R.drawable.stat_sys_data_in_g,
- com.android.internal.R.drawable.stat_sys_data_out_g,
- com.android.internal.R.drawable.stat_sys_data_inandout_g,
- };
- private static final int[] sDataNetType_3g = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_3g,
- com.android.internal.R.drawable.stat_sys_data_in_3g,
- com.android.internal.R.drawable.stat_sys_data_out_3g,
- com.android.internal.R.drawable.stat_sys_data_inandout_3g,
- };
- private static final int[] sDataNetType_e = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_e,
- com.android.internal.R.drawable.stat_sys_data_in_e,
- com.android.internal.R.drawable.stat_sys_data_out_e,
- com.android.internal.R.drawable.stat_sys_data_inandout_e,
- };
- //3.5G
- private static final int[] sDataNetType_h = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_h,
- com.android.internal.R.drawable.stat_sys_data_in_h,
- com.android.internal.R.drawable.stat_sys_data_out_h,
- com.android.internal.R.drawable.stat_sys_data_inandout_h,
- };
-
- //CDMA
- // Use 3G icons for EVDO data and 1x icons for 1XRTT data
- private static final int[] sDataNetType_1x = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_1x,
- com.android.internal.R.drawable.stat_sys_data_in_1x,
- com.android.internal.R.drawable.stat_sys_data_out_1x,
- com.android.internal.R.drawable.stat_sys_data_inandout_1x,
- };
-
- // Assume it's all good unless we hear otherwise. We don't always seem
- // to get broadcasts that it *is* there.
- IccCard.State mSimState = IccCard.State.READY;
- int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
- int mDataState = TelephonyManager.DATA_DISCONNECTED;
- int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
- ServiceState mServiceState;
- SignalStrength mSignalStrength;
-
- // data connection
- private IBinder mDataIcon;
- private IconData mDataData;
- private boolean mDataIconVisible;
- private boolean mHspaDataDistinguishable;
-
- // ringer volume
- private IBinder mVolumeIcon;
- private IconData mVolumeData;
- private boolean mVolumeVisible;
-
- // bluetooth device status
- private IBinder mBluetoothIcon;
- private IconData mBluetoothData;
- private int mBluetoothHeadsetState;
- private boolean mBluetoothA2dpConnected;
- private int mBluetoothPbapState;
- private boolean mBluetoothEnabled;
-
- // wifi
- private static final int[] sWifiSignalImages = new int[] {
- com.android.internal.R.drawable.stat_sys_wifi_signal_1,
- com.android.internal.R.drawable.stat_sys_wifi_signal_2,
- com.android.internal.R.drawable.stat_sys_wifi_signal_3,
- com.android.internal.R.drawable.stat_sys_wifi_signal_4,
- };
- private static final int sWifiTemporarilyNotConnectedImage =
- com.android.internal.R.drawable.stat_sys_wifi_signal_0;
-
- private int mLastWifiSignalLevel = -1;
- private boolean mIsWifiConnected = false;
- private IBinder mWifiIcon;
- private IconData mWifiData;
-
- // gps
- private IBinder mGpsIcon;
- private IconData mGpsEnabledIconData;
- private IconData mGpsFixIconData;
-
- // alarm clock
- // Icon lit when clock is set
- private IBinder mAlarmClockIcon;
- private IconData mAlarmClockIconData;
-
- // sync state
- // If sync is active the SyncActive icon is displayed. If sync is not active but
- // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
- private IBinder mSyncActiveIcon;
- private IBinder mSyncFailingIcon;
-
- // TTY mode
- // Icon lit when TTY mode is enabled
- private IBinder mTTYModeIcon;
- private IconData mTTYModeEnableIconData;
-
- // Cdma Roaming Indicator, ERI
- private IBinder mCdmaRoamingIndicatorIcon;
- private IconData mCdmaRoamingIndicatorIconData;
-
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_TIME_TICK)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- updateBattery(intent);
- }
- else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
- updateClock();
- }
- else if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- String tz = intent.getStringExtra("time-zone");
- mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
- if (mClockFormat != null) {
- mClockFormat.setTimeZone(mCalendar.getTimeZone());
- }
- updateClock();
- }
- else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
- updateAlarm(intent);
- }
- else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
- updateSyncState(intent);
- }
- else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
- onBatteryLow(intent);
- }
- else if (action.equals(Intent.ACTION_BATTERY_OKAY)
- || action.equals(Intent.ACTION_POWER_CONNECTED)) {
- onBatteryOkay(intent);
- }
- else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
- action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) ||
- action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ||
- action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
- updateBluetooth(intent);
- }
- else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
- action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) ||
- action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- updateWifi(intent);
- }
- else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) ||
- action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION)) {
- updateGps(intent);
- }
- else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
- action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) {
- updateVolume();
- }
- else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
- updateSimState(intent);
- }
- else if (action.equals(TtyIntent.TTY_ENABLED_CHANGE_ACTION)) {
- updateTTY(intent);
- }
- }
- };
-
- private StatusBarPolicy(Context context, StatusBarService service) {
- mContext = context;
- mService = service;
- mSignalStrength = new SignalStrength();
- mBatteryStats = BatteryStatsService.getService();
-
- // clock
- mCalendar = Calendar.getInstance(TimeZone.getDefault());
- mClockData = IconData.makeText("clock", "");
- mClockIcon = service.addIcon(mClockData, null);
- updateClock();
-
- // storage
- mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
- mStorageManager.registerListener(
- new com.android.server.status.StorageNotification(context));
-
- // battery
- mBatteryData = IconData.makeIcon("battery",
- null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0);
- mBatteryIcon = service.addIcon(mBatteryData, null);
-
- // phone_signal
- mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
- mPhoneData = IconData.makeIcon("phone_signal",
- null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
- mPhoneIcon = service.addIcon(mPhoneData, null);
-
- // register for phone state notifications.
- ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_SERVICE_STATE
- | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
- | PhoneStateListener.LISTEN_CALL_STATE
- | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
- | PhoneStateListener.LISTEN_DATA_ACTIVITY);
-
- // data_connection
- mDataData = IconData.makeIcon("data_connection",
- null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
- mDataIcon = service.addIcon(mDataData, null);
- service.setIconVisibility(mDataIcon, false);
-
- // wifi
- mWifiData = IconData.makeIcon("wifi", null, sWifiSignalImages[0], 0, 0);
- mWifiIcon = service.addIcon(mWifiData, null);
- service.setIconVisibility(mWifiIcon, false);
- // wifi will get updated by the sticky intents
-
- // TTY status
- mTTYModeEnableIconData = IconData.makeIcon("tty",
- null, com.android.internal.R.drawable.stat_sys_tty_mode, 0, 0);
- mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null);
- service.setIconVisibility(mTTYModeIcon, false);
-
- // Cdma Roaming Indicator, ERI
- mCdmaRoamingIndicatorIconData = IconData.makeIcon("cdma_eri",
- null, com.android.internal.R.drawable.stat_sys_roaming_cdma_0, 0, 0);
- mCdmaRoamingIndicatorIcon = service.addIcon(mCdmaRoamingIndicatorIconData, null);
- service.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
-
- // bluetooth status
- mBluetoothData = IconData.makeIcon("bluetooth",
- null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
- mBluetoothIcon = service.addIcon(mBluetoothData, null);
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- mBluetoothEnabled = adapter.isEnabled();
- } else {
- mBluetoothEnabled = false;
- }
- mBluetoothA2dpConnected = false;
- mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
- mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
- mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
-
- // Gps status
- mGpsEnabledIconData = IconData.makeIcon("gps",
- null, com.android.internal.R.drawable.stat_sys_gps_acquiring_anim, 0, 0);
- mGpsFixIconData = IconData.makeIcon("gps",
- null, com.android.internal.R.drawable.stat_sys_gps_on, 0, 0);
- mGpsIcon = service.addIcon(mGpsEnabledIconData, null);
- service.setIconVisibility(mGpsIcon, false);
-
- // Alarm clock
- mAlarmClockIconData = IconData.makeIcon(
- "alarm_clock",
- null, com.android.internal.R.drawable.stat_notify_alarm, 0, 0);
- mAlarmClockIcon = service.addIcon(mAlarmClockIconData, null);
- service.setIconVisibility(mAlarmClockIcon, false);
-
- // Sync state
- mSyncActiveIcon = service.addIcon(IconData.makeIcon("sync_active",
- null, R.drawable.stat_notify_sync_anim0, 0, 0), null);
- mSyncFailingIcon = service.addIcon(IconData.makeIcon("sync_failing",
- null, R.drawable.stat_notify_sync_error, 0, 0), null);
- service.setIconVisibility(mSyncActiveIcon, false);
- service.setIconVisibility(mSyncFailingIcon, false);
-
- // volume
- mVolumeData = IconData.makeIcon("volume",
- null, com.android.internal.R.drawable.stat_sys_ringer_silent, 0, 0);
- mVolumeIcon = service.addIcon(mVolumeData, null);
- service.setIconVisibility(mVolumeIcon, false);
- updateVolume();
-
- IntentFilter filter = new IntentFilter();
-
- // Register for Intent broadcasts for...
- filter.addAction(Intent.ACTION_TIME_TICK);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_LOW);
- filter.addAction(Intent.ACTION_BATTERY_OKAY);
- filter.addAction(Intent.ACTION_POWER_CONNECTED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_ALARM_CHANGED);
- filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
- filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
- filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- filter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
- filter.addAction(GpsLocationProvider.GPS_FIX_CHANGE_ACTION);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
- mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
-
- // load config to determine if to distinguish Hspa data icon
- try {
- mHspaDataDistinguishable = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_hspa_data_distinguishable);
- } catch (Exception e) {
- mHspaDataDistinguishable = false;
- }
- }
-
- public static void installIcons(Context context, StatusBarService service) {
- sInstance = new StatusBarPolicy(context, service);
- }
-
- private final CharSequence getSmallTime() {
- boolean b24 = DateFormat.is24HourFormat(mContext);
- int res;
-
- if (b24) {
- res = R.string.twenty_four_hour_time_format;
- } else {
- res = R.string.twelve_hour_time_format;
- }
-
- final char MAGIC1 = '\uEF00';
- final char MAGIC2 = '\uEF01';
-
- SimpleDateFormat sdf;
- String format = mContext.getString(res);
- if (!format.equals(mClockFormatString)) {
- /*
- * Search for an unquoted "a" in the format string, so we can
- * add dummy characters around it to let us find it again after
- * formatting and change its size.
- */
- int a = -1;
- boolean quoted = false;
- for (int i = 0; i < format.length(); i++) {
- char c = format.charAt(i);
-
- if (c == '\'') {
- quoted = !quoted;
- }
-
- if (!quoted && c == 'a') {
- a = i;
- break;
- }
- }
-
- if (a >= 0) {
- // Move a back so any whitespace before the AM/PM is also in the alternate size.
- final int b = a;
- while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
- a--;
- }
- format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
- + "a" + MAGIC2 + format.substring(b + 1);
- }
-
- mClockFormat = sdf = new SimpleDateFormat(format);
- mClockFormatString = format;
- } else {
- sdf = mClockFormat;
- }
- String result = sdf.format(mCalendar.getTime());
-
- int magic1 = result.indexOf(MAGIC1);
- int magic2 = result.indexOf(MAGIC2);
-
- if (magic1 >= 0 && magic2 > magic1) {
- SpannableStringBuilder formatted = new SpannableStringBuilder(result);
-
- formatted.setSpan(new RelativeSizeSpan(0.7f), magic1, magic2,
- Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
-
- formatted.delete(magic2, magic2 + 1);
- formatted.delete(magic1, magic1 + 1);
-
- return formatted;
- } else {
- return result;
- }
- }
-
- private final void updateClock() {
- mCalendar.setTimeInMillis(System.currentTimeMillis());
- mClockData.text = getSmallTime();
- mService.updateIcon(mClockIcon, mClockData, null);
- }
-
- private final void updateAlarm(Intent intent) {
- boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
- mService.setIconVisibility(mAlarmClockIcon, alarmSet);
- }
-
- private final void updateSyncState(Intent intent) {
- boolean isActive = intent.getBooleanExtra("active", false);
- boolean isFailing = intent.getBooleanExtra("failing", false);
- mService.setIconVisibility(mSyncActiveIcon, isActive);
- // Don't display sync failing icon: BUG 1297963 Set sync error timeout to "never"
- //mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
- }
-
- private final void updateBattery(Intent intent) {
- mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
- mBatteryData.iconLevel = intent.getIntExtra("level", 0);
- mService.updateIcon(mBatteryIcon, mBatteryData, null);
-
- boolean plugged = intent.getIntExtra("plugged", 0) != 0;
- int level = intent.getIntExtra("level", -1);
- if (false) {
- Slog.d(TAG, "updateBattery level=" + level
- + " plugged=" + plugged
- + " mBatteryPlugged=" + mBatteryPlugged
- + " mBatteryLevel=" + mBatteryLevel
- + " mBatteryFirst=" + mBatteryFirst);
- }
-
- boolean oldPlugged = mBatteryPlugged;
-
- mBatteryPlugged = plugged;
- mBatteryLevel = level;
-
- if (mBatteryFirst) {
- mBatteryFirst = false;
- }
- /*
- * No longer showing the battery view because it draws attention away
- * from the USB storage notification. We could still show it when
- * connected to a brick, but that could lead to the user into thinking
- * the device does not charge when plugged into USB (since he/she would
- * not see the same battery screen on USB as he sees on brick).
- */
- /* else {
- if (plugged && !oldPlugged) {
- showBatteryView();
- }
- }
- */
- if (false) {
- Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
- }
- }
-
- private void onBatteryLow(Intent intent) {
- if (SHOW_LOW_BATTERY_WARNING) {
- if (false) {
- Slog.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
-
- if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- showLowBatteryWarning();
- } else {
- mBatteryShowLowOnEndCall = true;
- }
- }
- }
-
- private void onBatteryOkay(Intent intent) {
- if (mLowBatteryDialog != null
- && SHOW_LOW_BATTERY_WARNING) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = false;
- }
- }
-
- private void showBatteryView() {
- closeLastBatteryView();
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- }
-
- int level = mBatteryLevel;
-
- View v = View.inflate(mContext, com.android.internal.R.layout.battery_status, null);
- mBatteryView = v;
- int pixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = v.getBackground();
- if (bg != null) {
- pixelFormat = bg.getOpacity();
- }
-
- int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
- }
-
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_TOAST,
- flags, pixelFormat);
-
- // Get the dim amount from the theme
- TypedArray a = mContext.obtainStyledAttributes(
- com.android.internal.R.styleable.Theme);
- lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
- a.recycle();
-
- lp.setTitle("Battery");
-
- TextView levelTextView = (TextView)v.findViewById(com.android.internal.R.id.level_percent);
- levelTextView.setText(mContext.getString(
- com.android.internal.R.string.battery_status_text_percent_format, level));
-
- setBatteryLevel(v, com.android.internal.R.id.spacer, 100-level, 0, 0);
- setBatteryLevel(v, com.android.internal.R.id.level, level,
- com.android.internal.R.drawable.battery_charge_fill, level);
-
- WindowManagerImpl.getDefault().addView(v, lp);
-
- scheduleCloseBatteryView();
- }
-
- private void setBatteryLevel(View parent, int id, int height, int background, int level) {
- ImageView v = (ImageView)parent.findViewById(id);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
- lp.weight = height;
- if (background != 0) {
- v.setBackgroundResource(background);
- Drawable bkg = v.getBackground();
- bkg.setLevel(level);
- }
- }
-
- private void showLowBatteryWarning() {
- closeLastBatteryView();
-
- // Show exact battery level.
- CharSequence levelText = mContext.getString(
- com.android.internal.R.string.battery_low_percent_format, mBatteryLevel);
-
- if (mBatteryLevelTextView != null) {
- mBatteryLevelTextView.setText(levelText);
- } else {
- View v = View.inflate(mContext, com.android.internal.R.layout.battery_low, null);
- mBatteryLevelTextView=(TextView)v.findViewById(com.android.internal.R.id.level_percent);
-
- mBatteryLevelTextView.setText(levelText);
-
- AlertDialog.Builder b = new AlertDialog.Builder(mContext);
- b.setCancelable(true);
- b.setTitle(com.android.internal.R.string.battery_low_title);
- b.setView(v);
- b.setIcon(android.R.drawable.ic_dialog_alert);
- b.setPositiveButton(android.R.string.ok, null);
-
- final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NO_HISTORY);
- if (intent.resolveActivity(mContext.getPackageManager()) != null) {
- b.setNegativeButton(com.android.internal.R.string.battery_low_why,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- mContext.startActivity(intent);
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- }
- }
- });
- }
-
- AlertDialog d = b.create();
- d.setOnDismissListener(mLowBatteryListener);
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- d.show();
- mLowBatteryDialog = d;
- }
-
- final ContentResolver cr = mContext.getContentResolver();
- if (Settings.System.getInt(cr,
- Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
- {
- final String soundPath = Settings.System.getString(cr,
- Settings.System.LOW_BATTERY_SOUND);
- if (soundPath != null) {
- final Uri soundUri = Uri.parse("file://" + soundPath);
- if (soundUri != null) {
- final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
- if (sfx != null) {
- sfx.setStreamType(AudioManager.STREAM_SYSTEM);
- sfx.play();
- }
- }
- }
- }
- }
-
- private final void updateCallState(int state) {
- mPhoneState = state;
- if (false) {
- Slog.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
- if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- if (mBatteryShowLowOnEndCall) {
- if (!mBatteryPlugged) {
- showLowBatteryWarning();
- }
- mBatteryShowLowOnEndCall = false;
- }
- } else {
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = true;
- }
- }
- }
-
- private DialogInterface.OnDismissListener mLowBatteryListener
- = new DialogInterface.OnDismissListener() {
- public void onDismiss(DialogInterface dialog) {
- mLowBatteryDialog = null;
- mBatteryLevelTextView = null;
- }
- };
-
- private void scheduleCloseBatteryView() {
- Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
- m.arg1 = (++mBatteryViewSequence);
- mHandler.sendMessageDelayed(m, 3000);
- }
-
- private void closeLastBatteryView() {
- if (mBatteryView != null) {
- //mBatteryView.debug();
- WindowManagerImpl.getDefault().removeView(mBatteryView);
- mBatteryView = null;
- }
- }
-
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- mSignalStrength = signalStrength;
- updateSignalStrength();
- }
-
- @Override
- public void onServiceStateChanged(ServiceState state) {
- mServiceState = state;
- updateSignalStrength();
- updateCdmaRoamingIcon(state);
- updateDataIcon();
- }
-
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- updateCallState(state);
- // In cdma, if a voice call is made, RSSI should switch to 1x.
- if (isCdma()) {
- updateSignalStrength();
- }
- }
-
- @Override
- public void onDataConnectionStateChanged(int state, int networkType) {
- mDataState = state;
- updateDataNetType(networkType);
- updateDataIcon();
- }
-
- @Override
- public void onDataActivity(int direction) {
- mDataActivity = direction;
- updateDataIcon();
- }
- };
-
- private final void updateSimState(Intent intent) {
- String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
- if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
- mSimState = IccCard.State.ABSENT;
- }
- else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
- mSimState = IccCard.State.READY;
- }
- else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
- final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
- if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
- mSimState = IccCard.State.PIN_REQUIRED;
- }
- else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
- mSimState = IccCard.State.PUK_REQUIRED;
- }
- else {
- mSimState = IccCard.State.NETWORK_LOCKED;
- }
- } else {
- mSimState = IccCard.State.UNKNOWN;
- }
- updateDataIcon();
- }
-
- private boolean isCdma() {
- return (mSignalStrength != null) && !mSignalStrength.isGsm();
- }
-
- private boolean isEvdo() {
- return ( (mServiceState != null)
- && ((mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
- || (mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
- || (mServiceState.getRadioTechnology()
- == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
- }
-
- private boolean hasService() {
- if (mServiceState != null) {
- switch (mServiceState.getState()) {
- case ServiceState.STATE_OUT_OF_SERVICE:
- case ServiceState.STATE_POWER_OFF:
- return false;
- default:
- return true;
- }
- } else {
- return false;
- }
- }
-
- private final void updateSignalStrength() {
- int iconLevel = -1;
- int[] iconList;
-
- // Display signal strength while in "emergency calls only" mode
- if (!hasService() && !mServiceState.isEmergencyOnly()) {
- //Slog.d(TAG, "updateSignalStrength: no service");
- if (Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
- mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_flightmode;
- } else {
- mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
- }
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- return;
- }
-
- if (!isCdma()) {
- int asu = mSignalStrength.getGsmSignalStrength();
-
- // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
- // asu = 0 (-113dB or less) is very weak
- // signal, its better to show 0 bars to the user in such cases.
- // asu = 99 is a special case, where the signal strength is unknown.
- if (asu <= 2 || asu == 99) iconLevel = 0;
- else if (asu >= 12) iconLevel = 4;
- else if (asu >= 8) iconLevel = 3;
- else if (asu >= 5) iconLevel = 2;
- else iconLevel = 1;
-
- // Though mPhone is a Manager, this call is not an IPC
- if (mPhone.isNetworkRoaming()) {
- iconList = sSignalImages_r;
- } else {
- iconList = sSignalImages;
- }
- } else {
- iconList = this.sSignalImages;
-
- // If 3G(EV) and 1x network are available than 3G should be
- // displayed, displayed RSSI should be from the EV side.
- // If a voice call is made then RSSI should switch to 1x.
- if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
- iconLevel = getEvdoLevel();
- if (false) {
- Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel());
- }
- } else {
- iconLevel = getCdmaLevel();
- }
- }
- mPhoneData.iconId = iconList[iconLevel];
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- }
-
- private int getCdmaLevel() {
- final int cdmaDbm = mSignalStrength.getCdmaDbm();
- final int cdmaEcio = mSignalStrength.getCdmaEcio();
- int levelDbm = 0;
- int levelEcio = 0;
-
- if (cdmaDbm >= -75) levelDbm = 4;
- else if (cdmaDbm >= -85) levelDbm = 3;
- else if (cdmaDbm >= -95) levelDbm = 2;
- else if (cdmaDbm >= -100) levelDbm = 1;
- else levelDbm = 0;
-
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) levelEcio = 4;
- else if (cdmaEcio >= -110) levelEcio = 3;
- else if (cdmaEcio >= -130) levelEcio = 2;
- else if (cdmaEcio >= -150) levelEcio = 1;
- else levelEcio = 0;
-
- return (levelDbm < levelEcio) ? levelDbm : levelEcio;
- }
-
- private int getEvdoLevel() {
- int evdoDbm = mSignalStrength.getEvdoDbm();
- int evdoSnr = mSignalStrength.getEvdoSnr();
- int levelEvdoDbm = 0;
- int levelEvdoSnr = 0;
-
- if (evdoDbm >= -65) levelEvdoDbm = 4;
- else if (evdoDbm >= -75) levelEvdoDbm = 3;
- else if (evdoDbm >= -90) levelEvdoDbm = 2;
- else if (evdoDbm >= -105) levelEvdoDbm = 1;
- else levelEvdoDbm = 0;
-
- if (evdoSnr >= 7) levelEvdoSnr = 4;
- else if (evdoSnr >= 5) levelEvdoSnr = 3;
- else if (evdoSnr >= 3) levelEvdoSnr = 2;
- else if (evdoSnr >= 1) levelEvdoSnr = 1;
- else levelEvdoSnr = 0;
-
- return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
- }
-
- private final void updateDataNetType(int net) {
- switch (net) {
- case TelephonyManager.NETWORK_TYPE_EDGE:
- mDataIconList = sDataNetType_e;
- break;
- case TelephonyManager.NETWORK_TYPE_UMTS:
- mDataIconList = sDataNetType_3g;
- break;
- case TelephonyManager.NETWORK_TYPE_HSDPA:
- case TelephonyManager.NETWORK_TYPE_HSUPA:
- case TelephonyManager.NETWORK_TYPE_HSPA:
- if (mHspaDataDistinguishable) {
- mDataIconList = sDataNetType_h;
- } else {
- mDataIconList = sDataNetType_3g;
- }
- break;
- case TelephonyManager.NETWORK_TYPE_CDMA:
- // display 1xRTT for IS95A/B
- mDataIconList = this.sDataNetType_1x;
- break;
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- mDataIconList = this.sDataNetType_1x;
- break;
- case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
- case TelephonyManager.NETWORK_TYPE_EVDO_A:
- case TelephonyManager.NETWORK_TYPE_EVDO_B:
- mDataIconList = sDataNetType_3g;
- break;
- default:
- mDataIconList = sDataNetType_g;
- break;
- }
- }
-
- private final void updateDataIcon() {
- int iconId;
- boolean visible = true;
-
- if (!isCdma()) {
- // GSM case, we have to check also the sim state
- if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
- if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
- switch (mDataActivity) {
- case TelephonyManager.DATA_ACTIVITY_IN:
- iconId = mDataIconList[1];
- break;
- case TelephonyManager.DATA_ACTIVITY_OUT:
- iconId = mDataIconList[2];
- break;
- case TelephonyManager.DATA_ACTIVITY_INOUT:
- iconId = mDataIconList[3];
- break;
- default:
- iconId = mDataIconList[0];
- break;
- }
- mDataData.iconId = iconId;
- mService.updateIcon(mDataIcon, mDataData, null);
- } else {
- visible = false;
- }
- } else {
- mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim;
- mService.updateIcon(mDataIcon, mDataData, null);
- }
- } else {
- // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
- if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
- switch (mDataActivity) {
- case TelephonyManager.DATA_ACTIVITY_IN:
- iconId = mDataIconList[1];
- break;
- case TelephonyManager.DATA_ACTIVITY_OUT:
- iconId = mDataIconList[2];
- break;
- case TelephonyManager.DATA_ACTIVITY_INOUT:
- iconId = mDataIconList[3];
- break;
- case TelephonyManager.DATA_ACTIVITY_DORMANT:
- default:
- iconId = mDataIconList[0];
- break;
- }
- mDataData.iconId = iconId;
- mService.updateIcon(mDataIcon, mDataData, null);
- } else {
- visible = false;
- }
- }
-
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- if (mDataIconVisible != visible) {
- mService.setIconVisibility(mDataIcon, visible);
- mDataIconVisible = visible;
- }
- }
-
- private final void updateVolume() {
- AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- final int ringerMode = audioManager.getRingerMode();
- final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
- ringerMode == AudioManager.RINGER_MODE_VIBRATE;
- final int iconId = (ringerMode == AudioManager.RINGER_MODE_VIBRATE)
- ? com.android.internal.R.drawable.stat_sys_ringer_vibrate
- : com.android.internal.R.drawable.stat_sys_ringer_silent;
-
- if (visible) {
- mVolumeData.iconId = iconId;
- mService.updateIcon(mVolumeIcon, mVolumeData, null);
- }
- if (visible != mVolumeVisible) {
- mService.setIconVisibility(mVolumeIcon, visible);
- mVolumeVisible = visible;
- }
- }
-
- private final void updateBluetooth(Intent intent) {
- int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
- String action = intent.getAction();
- if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
- mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
- } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
- mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
- BluetoothHeadset.STATE_ERROR);
- } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
- BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
- if (a2dp.getConnectedSinks().size() != 0) {
- mBluetoothA2dpConnected = true;
- } else {
- mBluetoothA2dpConnected = false;
- }
- } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
- mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
- BluetoothPbap.STATE_DISCONNECTED);
- } else {
- return;
- }
-
- if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected ||
- mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
- iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected;
- }
-
- mBluetoothData.iconId = iconId;
- mService.updateIcon(mBluetoothIcon, mBluetoothData, null);
- mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
- }
-
- private final void updateWifi(Intent intent) {
- final String action = intent.getAction();
- if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-
- final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-
- if (!enabled) {
- // If disabled, hide the icon. (We show icon when connected.)
- mService.setIconVisibility(mWifiIcon, false);
- }
-
- } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
- final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED,
- false);
- if (!enabled) {
- mService.setIconVisibility(mWifiIcon, false);
- }
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-
- final NetworkInfo networkInfo = (NetworkInfo)
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-
- int iconId;
- if (networkInfo != null && networkInfo.isConnected()) {
- mIsWifiConnected = true;
- if (mLastWifiSignalLevel == -1) {
- iconId = sWifiSignalImages[0];
- } else {
- iconId = sWifiSignalImages[mLastWifiSignalLevel];
- }
-
- // Show the icon since wi-fi is connected
- mService.setIconVisibility(mWifiIcon, true);
-
- } else {
- mLastWifiSignalLevel = -1;
- mIsWifiConnected = false;
- iconId = sWifiSignalImages[0];
-
- // Hide the icon since we're not connected
- mService.setIconVisibility(mWifiIcon, false);
- }
-
- mWifiData.iconId = iconId;
- mService.updateIcon(mWifiIcon, mWifiData, null);
- } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
- sWifiSignalImages.length);
- if (newSignalLevel != mLastWifiSignalLevel) {
- mLastWifiSignalLevel = newSignalLevel;
- if (mIsWifiConnected) {
- mWifiData.iconId = sWifiSignalImages[newSignalLevel];
- } else {
- mWifiData.iconId = sWifiTemporarilyNotConnectedImage;
- }
- mService.updateIcon(mWifiIcon, mWifiData, null);
- }
- }
- }
-
- private final void updateGps(Intent intent) {
- final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false);
-
- if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) {
- // GPS is getting fixes
- mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
- mService.setIconVisibility(mGpsIcon, true);
- } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
- // GPS is off
- mService.setIconVisibility(mGpsIcon, false);
- } else {
- // GPS is on, but not receiving fixes
- mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null);
- mService.setIconVisibility(mGpsIcon, true);
- }
- }
-
- private final void updateTTY(Intent intent) {
- final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
-
- if (false) Slog.v(TAG, "updateTTY: enabled: " + enabled);
-
- if (enabled) {
- // TTY is on
- if (false) Slog.v(TAG, "updateTTY: set TTY on");
- mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null);
- mService.setIconVisibility(mTTYModeIcon, true);
- } else {
- // TTY is off
- if (false) Slog.v(TAG, "updateTTY: set TTY off");
- mService.setIconVisibility(mTTYModeIcon, false);
- }
- }
-
- private final void updateCdmaRoamingIcon(ServiceState state) {
- if (!hasService()) {
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- if (!isCdma()) {
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- int[] iconList = sRoamingIndicatorImages_cdma;
- int iconIndex = state.getCdmaEriIconIndex();
- int iconMode = state.getCdmaEriIconMode();
-
- if (iconIndex == -1) {
- Slog.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update");
- return;
- }
-
- if (iconMode == -1) {
- Slog.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update");
- return;
- }
-
- if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) {
- if (false) Slog.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
- return;
- }
-
- switch (iconMode) {
- case EriInfo.ROAMING_ICON_MODE_NORMAL:
- mCdmaRoamingIndicatorIconData.iconId = iconList[iconIndex];
- mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
- break;
- case EriInfo.ROAMING_ICON_MODE_FLASH:
- mCdmaRoamingIndicatorIconData.iconId =
- com.android.internal.R.drawable.stat_sys_roaming_cdma_flash;
- mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
- mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
- break;
-
- }
- mService.updateIcon(mPhoneIcon, mPhoneData, null);
- }
-
-
- private class StatusBarHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_BATTERY_CLOSE:
- if (msg.arg1 == mBatteryViewSequence) {
- closeLastBatteryView();
- }
- break;
- }
- }
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
deleted file mode 100644
index 93c8d34..0000000
--- a/services/java/com/android/server/status/StatusBarService.java
+++ /dev/null
@@ -1,1881 +0,0 @@
-/*
- * Copyright (C) 2007 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.status;
-
-import com.android.internal.R;
-import com.android.internal.util.CharSequences;
-
-import android.app.ActivityManagerNative;
-import android.app.Dialog;
-import android.app.IStatusBar;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Telephony;
-import android.util.Slog;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.FrameLayout;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
-
-
-/**
- * The public (ok, semi-public) service for the status bar.
- * <p>
- * This interesting thing to note about this class is that most of the methods that
- * are called from other classes just post a message, and everything else is batched
- * and coalesced into a series of calls to methods that all start with "perform."
- * There are two reasons for this. The first is that some of the methods (activate/deactivate)
- * are on IStatusBar, so they're called from the thread pool and they need to make their
- * way onto the UI thread. The second is that the message queue is stopped while animations
- * are happening in order to make for smoother transitions.
- * <p>
- * Each icon is either an icon or an icon and a notification. They're treated mostly
- * separately throughout the code, although they both use the same key, which is assigned
- * when they are created.
- */
-public class StatusBarService extends IStatusBar.Stub
-{
- static final String TAG = "StatusBar";
- static final boolean SPEW = false;
-
- static final int EXPANDED_LEAVE_ALONE = -10000;
- static final int EXPANDED_FULL_OPEN = -10001;
-
- private static final int MSG_ANIMATE = 1000;
- private static final int MSG_ANIMATE_REVEAL = 1001;
-
- private static final int OP_ADD_ICON = 1;
- private static final int OP_UPDATE_ICON = 2;
- private static final int OP_REMOVE_ICON = 3;
- private static final int OP_SET_VISIBLE = 4;
- private static final int OP_EXPAND = 5;
- private static final int OP_TOGGLE = 6;
- private static final int OP_DISABLE = 7;
- private class PendingOp {
- IBinder key;
- int code;
- IconData iconData;
- NotificationData notificationData;
- boolean visible;
- int integer;
- }
-
- private class DisableRecord implements IBinder.DeathRecipient {
- String pkg;
- int what;
- IBinder token;
-
- public void binderDied() {
- Slog.i(TAG, "binder died for pkg=" + pkg);
- disable(0, token, pkg);
- token.unlinkToDeath(this, 0);
- }
- }
-
- public interface NotificationCallbacks {
- void onSetDisabled(int status);
- void onClearAll();
- void onNotificationClick(String pkg, String tag, int id);
- void onPanelRevealed();
- }
-
- private class ExpandedDialog extends Dialog {
- ExpandedDialog(Context context) {
- super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (!down) {
- StatusBarService.this.deactivate();
- }
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
- }
-
- final Context mContext;
- final Display mDisplay;
- StatusBarView mStatusBarView;
- int mPixelFormat;
- H mHandler = new H();
- Object mQueueLock = new Object();
- ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
- NotificationCallbacks mNotificationCallbacks;
-
- // All accesses to mIconMap and mNotificationData are syncronized on those objects,
- // but this is only so dump() can work correctly. Modifying these outside of the UI
- // thread will not work, there are places in the code that unlock and reaquire between
- // reads and require them to not be modified.
-
- // icons
- HashMap<IBinder,StatusBarIcon> mIconMap = new HashMap<IBinder,StatusBarIcon>();
- ArrayList<StatusBarIcon> mIconList = new ArrayList<StatusBarIcon>();
- String[] mRightIconSlots;
- StatusBarIcon[] mRightIcons;
- LinearLayout mIcons;
- IconMerger mNotificationIcons;
- LinearLayout mStatusIcons;
- StatusBarIcon mMoreIcon;
- private UninstallReceiver mUninstallReceiver;
-
- // expanded notifications
- NotificationViewList mNotificationData = new NotificationViewList();
- Dialog mExpandedDialog;
- ExpandedView mExpandedView;
- WindowManager.LayoutParams mExpandedParams;
- ScrollView mScrollView;
- View mNotificationLinearLayout;
- TextView mOngoingTitle;
- LinearLayout mOngoingItems;
- TextView mLatestTitle;
- LinearLayout mLatestItems;
- TextView mNoNotificationsTitle;
- TextView mSpnLabel;
- TextView mPlmnLabel;
- TextView mClearButton;
- View mExpandedContents;
- CloseDragHandle mCloseView;
- int[] mPositionTmp = new int[2];
- boolean mExpanded;
- boolean mExpandedVisible;
-
- // the date view
- DateView mDateView;
-
- // the tracker view
- TrackingView mTrackingView;
- WindowManager.LayoutParams mTrackingParams;
- int mTrackingPosition; // the position of the top of the tracking view.
-
- // ticker
- private Ticker mTicker;
- private View mTickerView;
- private boolean mTicking;
-
- // Tracking finger for opening/closing.
- int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
- boolean mTracking;
- VelocityTracker mVelocityTracker;
-
- static final int ANIM_FRAME_DURATION = (1000/60);
-
- boolean mAnimating;
- long mCurAnimationTime;
- float mDisplayHeight;
- float mAnimY;
- float mAnimVel;
- float mAnimAccel;
- long mAnimLastTime;
- boolean mAnimatingReveal = false;
- int mViewDelta;
- int[] mAbsPos = new int[2];
-
- // for disabling the status bar
- ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
- int mDisabled = 0;
-
- /**
- * Construct the service, add the status bar view to the window manager
- */
- public StatusBarService(Context context) {
- mContext = context;
- mDisplay = ((WindowManager)context.getSystemService(
- Context.WINDOW_SERVICE)).getDefaultDisplay();
- makeStatusBarView(context);
- mUninstallReceiver = new UninstallReceiver();
- }
-
- public void setNotificationCallbacks(NotificationCallbacks listener) {
- mNotificationCallbacks = listener;
- }
-
- // ================================================================================
- // Constructing the view
- // ================================================================================
- private void makeStatusBarView(Context context) {
- Resources res = context.getResources();
- mRightIconSlots = res.getStringArray(com.android.internal.R.array.status_bar_icon_order);
- mRightIcons = new StatusBarIcon[mRightIconSlots.length];
-
- ExpandedView expanded = (ExpandedView)View.inflate(context,
- com.android.internal.R.layout.status_bar_expanded, null);
- expanded.mService = this;
- StatusBarView sb = (StatusBarView)View.inflate(context,
- com.android.internal.R.layout.status_bar, null);
- sb.mService = this;
-
- // figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = sb.getBackground();
- if (bg != null) {
- mPixelFormat = bg.getOpacity();
- }
-
- mStatusBarView = sb;
- mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
- mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
- mNotificationIcons.service = this;
- mIcons = (LinearLayout)sb.findViewById(R.id.icons);
- mTickerView = sb.findViewById(R.id.ticker);
- mDateView = (DateView)sb.findViewById(R.id.date);
-
- mExpandedDialog = new ExpandedDialog(context);
- mExpandedView = expanded;
- mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
- mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
- mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
- mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
- mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
- mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
- mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
- mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel);
- mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel);
- mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
- mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
-
- mOngoingTitle.setVisibility(View.GONE);
- mLatestTitle.setVisibility(View.GONE);
-
- mTicker = new MyTicker(context, sb);
-
- TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
- tickerView.mTicker = mTicker;
-
- mTrackingView = (TrackingView)View.inflate(context,
- com.android.internal.R.layout.status_bar_tracking, null);
- mTrackingView.mService = this;
- mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
- mCloseView.mService = this;
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- // add the more icon for the notifications
- IconData moreData = IconData.makeIcon(null, context.getPackageName(),
- R.drawable.stat_notify_more, 0, 42);
- mMoreIcon = new StatusBarIcon(context, moreData, mNotificationIcons);
- mMoreIcon.view.setId(R.drawable.stat_notify_more);
- mNotificationIcons.moreIcon = mMoreIcon;
- mNotificationIcons.addView(mMoreIcon.view);
-
- // set the inital view visibility
- setAreThereNotifications();
- mDateView.setVisibility(View.INVISIBLE);
-
- // before we register for broadcasts
- mPlmnLabel.setText(R.string.lockscreen_carrier_default);
- mPlmnLabel.setVisibility(View.VISIBLE);
- mSpnLabel.setText("");
- mSpnLabel.setVisibility(View.GONE);
-
- // receive broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
- context.registerReceiver(mBroadcastReceiver, filter);
- }
-
- public void systemReady() {
- final StatusBarView view = mStatusBarView;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- view.getContext().getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height),
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
- mPixelFormat);
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("StatusBar");
- lp.windowAnimations = R.style.Animation_StatusBar;
-
- WindowManagerImpl.getDefault().addView(view, lp);
- }
-
- // ================================================================================
- // From IStatusBar
- // ================================================================================
- public void activate() {
- enforceExpandStatusBar();
- addPendingOp(OP_EXPAND, null, true);
- }
-
- public void deactivate() {
- enforceExpandStatusBar();
- addPendingOp(OP_EXPAND, null, false);
- }
-
- public void toggle() {
- enforceExpandStatusBar();
- addPendingOp(OP_TOGGLE, null, false);
- }
-
- public void disable(int what, IBinder token, String pkg) {
- enforceStatusBar();
- synchronized (mNotificationCallbacks) {
- // This is a little gross, but I think it's safe as long as nobody else
- // synchronizes on mNotificationCallbacks. It's important that the the callback
- // and the pending op get done in the correct order and not interleaved with
- // other calls, otherwise they'll get out of sync.
- int net;
- synchronized (mDisableRecords) {
- manageDisableListLocked(what, token, pkg);
- net = gatherDisableActionsLocked();
- mNotificationCallbacks.onSetDisabled(net);
- }
- addPendingOp(OP_DISABLE, net);
- }
- }
-
- public IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel) {
- enforceStatusBar();
- return addIcon(IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
- }
-
- public void updateIcon(IBinder key,
- String slot, String iconPackage, int iconId, int iconLevel) {
- enforceStatusBar();
- updateIcon(key, IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
- }
-
- public void removeIcon(IBinder key) {
- enforceStatusBar();
- addPendingOp(OP_REMOVE_ICON, key, null, null, -1);
- }
-
- private void enforceStatusBar() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.STATUS_BAR,
- "StatusBarService");
- }
-
- private void enforceExpandStatusBar() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.EXPAND_STATUS_BAR,
- "StatusBarService");
- }
-
- // ================================================================================
- // Can be called from any thread
- // ================================================================================
- public IBinder addIcon(IconData data, NotificationData n) {
- int slot;
- // assert early-on if they using a slot that doesn't exist.
- if (data != null && n == null) {
- slot = getRightIconIndex(data.slot);
- if (slot < 0) {
- throw new SecurityException("invalid status bar icon slot: "
- + (data.slot != null ? "'" + data.slot + "'" : "null"));
- }
- } else {
- slot = -1;
- }
- IBinder key = new Binder();
- addPendingOp(OP_ADD_ICON, key, data, n, -1);
- return key;
- }
-
- public void updateIcon(IBinder key, IconData data, NotificationData n) {
- addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
- }
-
- public void setIconVisibility(IBinder key, boolean visible) {
- addPendingOp(OP_SET_VISIBLE, key, visible);
- }
-
- private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.key = key;
- op.code = code;
- op.iconData = data == null ? null : data.clone();
- op.notificationData = n;
- op.integer = i;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(2);
- }
- }
- }
-
- private void addPendingOp(int code, IBinder key, boolean visible) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.key = key;
- op.code = code;
- op.visible = visible;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-
- private void addPendingOp(int code, int integer) {
- synchronized (mQueueLock) {
- PendingOp op = new PendingOp();
- op.code = code;
- op.integer = integer;
- mQueue.add(op);
- if (mQueue.size() == 1) {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-
- // lock on mDisableRecords
- void manageDisableListLocked(int what, IBinder token, String pkg) {
- if (SPEW) {
- Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what)
- + " pkg=" + pkg);
- }
- // update the list
- synchronized (mDisableRecords) {
- final int N = mDisableRecords.size();
- DisableRecord tok = null;
- int i;
- for (i=0; i<N; i++) {
- DisableRecord t = mDisableRecords.get(i);
- if (t.token == token) {
- tok = t;
- break;
- }
- }
- if (what == 0 || !token.isBinderAlive()) {
- if (tok != null) {
- mDisableRecords.remove(i);
- tok.token.unlinkToDeath(tok, 0);
- }
- } else {
- if (tok == null) {
- tok = new DisableRecord();
- try {
- token.linkToDeath(tok, 0);
- }
- catch (RemoteException ex) {
- return; // give up
- }
- mDisableRecords.add(tok);
- }
- tok.what = what;
- tok.token = token;
- tok.pkg = pkg;
- }
- }
- }
-
- // lock on mDisableRecords
- int gatherDisableActionsLocked() {
- final int N = mDisableRecords.size();
- // gather the new net flags
- int net = 0;
- for (int i=0; i<N; i++) {
- net |= mDisableRecords.get(i).what;
- }
- return net;
- }
-
- private int getRightIconIndex(String slot) {
- final int N = mRightIconSlots.length;
- for (int i=0; i<N; i++) {
- if (mRightIconSlots[i].equals(slot)) {
- return i;
- }
- }
- return -1;
- }
-
- // ================================================================================
- // Always called from UI thread
- // ================================================================================
- /**
- * All changes to the status bar and notifications funnel through here and are batched.
- */
- private class H extends Handler {
- public void handleMessage(Message m) {
- if (m.what == MSG_ANIMATE) {
- doAnimation();
- return;
- }
- if (m.what == MSG_ANIMATE_REVEAL) {
- doRevealAnimation();
- return;
- }
-
- ArrayList<PendingOp> queue;
- synchronized (mQueueLock) {
- queue = mQueue;
- mQueue = new ArrayList<PendingOp>();
- }
-
- boolean wasExpanded = mExpanded;
-
- // for each one in the queue, find all of the ones with the same key
- // and collapse that down into a final op and/or call to setVisibility, etc
- boolean expand = wasExpanded;
- boolean doExpand = false;
- boolean doDisable = false;
- int disableWhat = 0;
- int N = queue.size();
- while (N > 0) {
- PendingOp op = queue.get(0);
- boolean doOp = false;
- boolean visible = false;
- boolean doVisibility = false;
- if (op.code == OP_SET_VISIBLE) {
- doVisibility = true;
- visible = op.visible;
- }
- else if (op.code == OP_EXPAND) {
- doExpand = true;
- expand = op.visible;
- }
- else if (op.code == OP_TOGGLE) {
- doExpand = true;
- expand = !expand;
- }
- else {
- doOp = true;
- }
-
- if (alwaysHandle(op.code)) {
- // coalesce these
- for (int i=1; i<N; i++) {
- PendingOp o = queue.get(i);
- if (!alwaysHandle(o.code) && o.key == op.key) {
- if (o.code == OP_SET_VISIBLE) {
- visible = o.visible;
- doVisibility = true;
- }
- else if (o.code == OP_EXPAND) {
- expand = o.visible;
- doExpand = true;
- }
- else {
- op.code = o.code;
- op.iconData = o.iconData;
- op.notificationData = o.notificationData;
- }
- queue.remove(i);
- i--;
- N--;
- }
- }
- }
-
- queue.remove(0);
- N--;
-
- if (doOp) {
- switch (op.code) {
- case OP_ADD_ICON:
- case OP_UPDATE_ICON:
- performAddUpdateIcon(op.key, op.iconData, op.notificationData);
- break;
- case OP_REMOVE_ICON:
- performRemoveIcon(op.key);
- break;
- case OP_DISABLE:
- doDisable = true;
- disableWhat = op.integer;
- break;
- }
- }
- if (doVisibility && op.code != OP_REMOVE_ICON) {
- performSetIconVisibility(op.key, visible);
- }
- }
-
- if (queue.size() != 0) {
- throw new RuntimeException("Assertion failed: queue.size=" + queue.size());
- }
- if (doExpand) {
- // this is last so that we capture all of the pending changes before doing it
- if (expand) {
- animateExpand();
- } else {
- animateCollapse();
- }
- }
- if (doDisable) {
- performDisableActions(disableWhat);
- }
- }
- }
-
- private boolean alwaysHandle(int code) {
- return code == OP_DISABLE;
- }
-
- /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
- throws StatusBarException {
- if (SPEW) {
- Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
- }
- // notification
- if (n != null) {
- StatusBarNotification notification = getNotification(key);
- NotificationData oldData = null;
- if (notification == null) {
- // add
- notification = new StatusBarNotification();
- notification.key = key;
- notification.data = n;
- synchronized (mNotificationData) {
- mNotificationData.add(notification);
- }
- addNotificationView(notification);
- setAreThereNotifications();
- } else {
- // update
- oldData = notification.data;
- notification.data = n;
- updateNotificationView(notification, oldData);
- }
- // Show the ticker if one is requested, and the text is different
- // than the currently displayed ticker. Also don't do this
- // until status bar window is attached to the window manager,
- // because... well, what's the point otherwise? And trying to
- // run a ticker without being attached will crash!
- if (n.tickerText != null && mStatusBarView.getWindowToken() != null
- && (oldData == null
- || oldData.tickerText == null
- || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
- if (0 == (mDisabled &
- (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
- mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
- }
- }
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- }
-
- // icon
- synchronized (mIconMap) {
- StatusBarIcon icon = mIconMap.get(key);
- if (icon == null) {
- // add
- LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
-
- icon = new StatusBarIcon(mContext, data, v);
- mIconMap.put(key, icon);
- mIconList.add(icon);
-
- if (n == null) {
- int slotIndex = getRightIconIndex(data.slot);
- StatusBarIcon[] rightIcons = mRightIcons;
- if (rightIcons[slotIndex] == null) {
- int pos = 0;
- for (int i=mRightIcons.length-1; i>slotIndex; i--) {
- StatusBarIcon ic = rightIcons[i];
- if (ic != null) {
- pos++;
- }
- }
- rightIcons[slotIndex] = icon;
- mStatusIcons.addView(icon.view, pos);
- } else {
- Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
- mIconMap.remove(key);
- mIconList.remove(icon);
- return ;
- }
- } else {
- int iconIndex = mNotificationData.getIconIndex(n);
- mNotificationIcons.addView(icon.view, iconIndex);
- }
- } else {
- if (n == null) {
- // right hand side icons -- these don't reorder
- icon.update(mContext, data);
- } else {
- // remove old
- ViewGroup parent = (ViewGroup)icon.view.getParent();
- parent.removeView(icon.view);
- // add new
- icon.update(mContext, data);
- int iconIndex = mNotificationData.getIconIndex(n);
- mNotificationIcons.addView(icon.view, iconIndex);
- }
- }
- }
- }
-
- /* private */ void performSetIconVisibility(IBinder key, boolean visible) {
- synchronized (mIconMap) {
- if (SPEW) {
- Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
- }
- StatusBarIcon icon = mIconMap.get(key);
- icon.view.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
- }
-
- /* private */ void performRemoveIcon(IBinder key) {
- synchronized (this) {
- if (SPEW) {
- Slog.d(TAG, "performRemoveIcon key=" + key);
- }
- StatusBarIcon icon = mIconMap.remove(key);
- mIconList.remove(icon);
- if (icon != null) {
- ViewGroup parent = (ViewGroup)icon.view.getParent();
- parent.removeView(icon.view);
- int slotIndex = getRightIconIndex(icon.mData.slot);
- if (slotIndex >= 0) {
- mRightIcons[slotIndex] = null;
- }
- }
- StatusBarNotification notification = getNotification(key);
- if (notification != null) {
- removeNotificationView(notification);
- synchronized (mNotificationData) {
- mNotificationData.remove(notification);
- }
- setAreThereNotifications();
- }
- }
- }
-
- int getIconNumberForView(View v) {
- synchronized (mIconMap) {
- StatusBarIcon icon = null;
- final int N = mIconList.size();
- for (int i=0; i<N; i++) {
- StatusBarIcon ic = mIconList.get(i);
- if (ic.view == v) {
- icon = ic;
- break;
- }
- }
- if (icon != null) {
- return icon.getNumber();
- } else {
- return -1;
- }
- }
- }
-
-
- StatusBarNotification getNotification(IBinder key) {
- synchronized (mNotificationData) {
- return mNotificationData.get(key);
- }
- }
-
- View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
- public void onFocusChange(View v, boolean hasFocus) {
- // Because 'v' is a ViewGroup, all its children will be (un)selected
- // too, which allows marqueeing to work.
- v.setSelected(hasFocus);
- }
- };
-
- View makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
- NotificationData n = notification.data;
- RemoteViews remoteViews = n.contentView;
- if (remoteViews == null) {
- return null;
- }
-
- // create the row view
- LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event, parent, false);
-
- // bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content);
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- content.setOnFocusChangeListener(mFocusChangeListener);
- PendingIntent contentIntent = n.contentIntent;
- if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
- }
-
- View child = null;
- Exception exception = null;
- try {
- child = remoteViews.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (child == null) {
- Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception);
- return null;
- }
- content.addView(child);
-
- row.setDrawingCacheEnabled(true);
-
- notification.view = row;
- notification.contentView = child;
-
- return row;
- }
-
- void addNotificationView(StatusBarNotification notification) {
- if (notification.view != null) {
- throw new RuntimeException("Assertion failed: notification.view="
- + notification.view);
- }
-
- LinearLayout parent = notification.data.ongoingEvent ? mOngoingItems : mLatestItems;
-
- View child = makeNotificationView(notification, parent);
- if (child == null) {
- return ;
- }
-
- int index = mNotificationData.getExpandedIndex(notification);
- parent.addView(child, index);
- }
-
- /**
- * Remove the old one and put the new one in its place.
- * @param notification the notification
- */
- void updateNotificationView(StatusBarNotification notification, NotificationData oldData) {
- NotificationData n = notification.data;
- if (oldData != null && n != null
- && n.when == oldData.when
- && n.ongoingEvent == oldData.ongoingEvent
- && n.contentView != null && oldData.contentView != null
- && n.contentView.getPackage() != null
- && oldData.contentView.getPackage() != null
- && oldData.contentView.getPackage().equals(n.contentView.getPackage())
- && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
- && notification.view != null) {
- mNotificationData.update(notification);
- try {
- n.contentView.reapply(mContext, notification.contentView);
-
- // update the contentIntent
- ViewGroup content = (ViewGroup)notification.view.findViewById(
- com.android.internal.R.id.content);
- PendingIntent contentIntent = n.contentIntent;
- if (contentIntent != null) {
- content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
- }
- }
- catch (RuntimeException e) {
- // It failed to add cleanly. Log, and remove the view from the panel.
- Slog.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e);
- removeNotificationView(notification);
- }
- } else {
- mNotificationData.update(notification);
- removeNotificationView(notification);
- addNotificationView(notification);
- }
- setAreThereNotifications();
- }
-
- void removeNotificationView(StatusBarNotification notification) {
- View v = notification.view;
- if (v != null) {
- ViewGroup parent = (ViewGroup)v.getParent();
- parent.removeView(v);
- notification.view = null;
- }
- }
-
- private void setAreThereNotifications() {
- boolean ongoing = mOngoingItems.getChildCount() != 0;
- boolean latest = mLatestItems.getChildCount() != 0;
-
- if (mNotificationData.hasClearableItems()) {
- mClearButton.setVisibility(View.VISIBLE);
- } else {
- mClearButton.setVisibility(View.INVISIBLE);
- }
-
- mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
- mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
-
- if (ongoing || latest) {
- mNoNotificationsTitle.setVisibility(View.GONE);
- } else {
- mNoNotificationsTitle.setVisibility(View.VISIBLE);
- }
- }
-
- private void makeExpandedVisible() {
- if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
- if (mExpandedVisible) {
- return;
- }
- mExpandedVisible = true;
- panelSlightlyVisible(true);
-
- updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- mExpandedView.requestFocus(View.FOCUS_FORWARD);
- mTrackingView.setVisibility(View.VISIBLE);
-
- if (!mTicking) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
-
- void animateExpand() {
- if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return ;
- }
- if (mExpanded) {
- return;
- }
-
- prepareTracking(0, true);
- performFling(0, 2000.0f, true);
- }
-
- void animateCollapse() {
- if (SPEW) {
- Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible
- + " mExpanded=" + mExpanded
- + " mAnimating=" + mAnimating
- + " mAnimY=" + mAnimY
- + " mAnimVel=" + mAnimVel);
- }
-
- if (!mExpandedVisible) {
- return;
- }
-
- int y;
- if (mAnimating) {
- y = (int)mAnimY;
- } else {
- y = mDisplay.getHeight()-1;
- }
- // Let the fling think that we're open so it goes in the right direction
- // and doesn't try to re-open the windowshade.
- mExpanded = true;
- prepareTracking(y, false);
- performFling(y, -2000.0f, true);
- }
-
- void performExpand() {
- if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return ;
- }
- if (mExpanded) {
- return;
- }
-
- // It seems strange to sometimes not expand...
- if (false) {
- synchronized (mNotificationData) {
- if (mNotificationData.size() == 0) {
- return;
- }
- }
- }
-
- mExpanded = true;
- makeExpandedVisible();
- updateExpandedViewPos(EXPANDED_FULL_OPEN);
-
- if (false) postStartTracing();
- }
-
- void performCollapse() {
- if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
- + " mExpandedVisible=" + mExpandedVisible);
-
- if (!mExpandedVisible) {
- return;
- }
- mExpandedVisible = false;
- panelSlightlyVisible(false);
- mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- mTrackingView.setVisibility(View.GONE);
-
- if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
-
- if (!mExpanded) {
- return;
- }
- mExpanded = false;
- }
-
- void doAnimation() {
- if (mAnimating) {
- if (SPEW) Slog.d(TAG, "doAnimation");
- if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
- incrementAnim();
- if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
- if (mAnimY >= mDisplay.getHeight()-1) {
- if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
- mAnimating = false;
- updateExpandedViewPos(EXPANDED_FULL_OPEN);
- performExpand();
- }
- else if (mAnimY < mStatusBarView.getHeight()) {
- if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
- mAnimating = false;
- updateExpandedViewPos(0);
- performCollapse();
- }
- else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- }
- }
- }
-
- void stopTracking() {
- mTracking = false;
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- void incrementAnim() {
- long now = SystemClock.uptimeMillis();
- float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
- final float y = mAnimY;
- final float v = mAnimVel; // px/s
- final float a = mAnimAccel; // px/s/s
- mAnimY = y + (v*t) + (0.5f*a*t*t); // px
- mAnimVel = v + (a*t); // px/s
- mAnimLastTime = now; // ms
- //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
- // + " mAnimAccel=" + mAnimAccel);
- }
-
- void doRevealAnimation() {
- final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
- if (mAnimatingReveal && mAnimating && mAnimY < h) {
- incrementAnim();
- if (mAnimY >= h) {
- mAnimY = h;
- updateExpandedViewPos((int)mAnimY);
- } else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- }
- }
- }
-
- void prepareTracking(int y, boolean opening) {
- mTracking = true;
- mVelocityTracker = VelocityTracker.obtain();
- if (opening) {
- mAnimAccel = 2000.0f;
- mAnimVel = 200;
- mAnimY = mStatusBarView.getHeight();
- updateExpandedViewPos((int)mAnimY);
- mAnimating = true;
- mAnimatingReveal = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- makeExpandedVisible();
- } else {
- // it's open, close it?
- if (mAnimating) {
- mAnimating = false;
- mHandler.removeMessages(MSG_ANIMATE);
- }
- updateExpandedViewPos(y + mViewDelta);
- }
- }
-
- void performFling(int y, float vel, boolean always) {
- mAnimatingReveal = false;
- mDisplayHeight = mDisplay.getHeight();
-
- mAnimY = y;
- mAnimVel = vel;
-
- //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
- if (mExpanded) {
- if (!always && (
- vel > 200.0f
- || (y > (mDisplayHeight-25) && vel > -200.0f))) {
- // We are expanded, but they didn't move sufficiently to cause
- // us to retract. Animate back to the expanded position.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are expanded and are now going to animate away.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- } else {
- if (always || (
- vel > 200.0f
- || (y > (mDisplayHeight/2) && vel > -200.0f))) {
- // We are collapsed, and they moved enough to allow us to
- // expand. Animate in the notifications.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are collapsed, but they didn't move sufficiently to cause
- // us to retract. Animate back to the collapsed position.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- }
- //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
- // + " mAnimAccel=" + mAnimAccel);
-
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- stopTracking();
- }
-
- boolean interceptTouchEvent(MotionEvent event) {
- if (SPEW) {
- Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
- + mDisabled);
- }
-
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return false;
- }
-
- final int statusBarSize = mStatusBarView.getHeight();
- final int hitSize = statusBarSize*2;
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final int y = (int)event.getRawY();
-
- if (!mExpanded) {
- mViewDelta = statusBarSize - y;
- } else {
- mTrackingView.getLocationOnScreen(mAbsPos);
- mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
- }
- if ((!mExpanded && y < hitSize) ||
- (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
-
- // We drop events at the edge of the screen to make the windowshade come
- // down by accident less, especially when pushing open a device with a keyboard
- // that rotates (like g1 and droid)
- int x = (int)event.getRawX();
- final int edgeBorder = mEdgeBorder;
- if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
- prepareTracking(y, !mExpanded);// opening if we're not already fully visible
- mVelocityTracker.addMovement(event);
- }
- }
- } else if (mTracking) {
- mVelocityTracker.addMovement(event);
- final int minY = statusBarSize + mCloseView.getHeight();
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- int y = (int)event.getRawY();
- if (mAnimatingReveal && y < minY) {
- // nothing
- } else {
- mAnimatingReveal = false;
- updateExpandedViewPos(y + mViewDelta);
- }
- } else if (event.getAction() == MotionEvent.ACTION_UP) {
- mVelocityTracker.computeCurrentVelocity(1000);
-
- float yVel = mVelocityTracker.getYVelocity();
- boolean negative = yVel < 0;
-
- float xVel = mVelocityTracker.getXVelocity();
- if (xVel < 0) {
- xVel = -xVel;
- }
- if (xVel > 150.0f) {
- xVel = 150.0f; // limit how much we care about the x axis
- }
-
- float vel = (float)Math.hypot(yVel, xVel);
- if (negative) {
- vel = -vel;
- }
-
- performFling((int)event.getRawY(), vel, false);
- }
-
- }
- return false;
- }
-
- private class Launcher implements View.OnClickListener {
- private PendingIntent mIntent;
- private String mPkg;
- private String mTag;
- private int mId;
-
- Launcher(PendingIntent intent, String pkg, String tag, int id) {
- mIntent = intent;
- mPkg = pkg;
- mTag = tag;
- mId = id;
- }
-
- public void onClick(View v) {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManagerNative.getDefault().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- Intent overlay = new Intent();
- overlay.setSourceBounds(
- new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
- try {
- mIntent.send(mContext, 0, overlay);
- mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId);
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here. Just log the exception message.
- Slog.w(TAG, "Sending contentIntent failed: " + e);
- }
- deactivate();
- }
- }
-
- private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
- super(context, sb);
- }
-
- @Override
- void tickerStarting() {
- mTicking = true;
- mIcons.setVisibility(View.GONE);
- mTickerView.setVisibility(View.VISIBLE);
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
- if (mExpandedVisible) {
- setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
- }
- }
-
- @Override
- void tickerDone() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
- }
- }
-
- void tickerHalting() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- }
-
- Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
- public void onAnimationEnd(Animation animation) {
- mTicking = false;
- }
- public void onAnimationRepeat(Animation animation) {
- }
- public void onAnimationStart(Animation animation) {
- }
- };
-
- private Animation loadAnim(int id, Animation.AnimationListener listener) {
- Animation anim = AnimationUtils.loadAnimation(mContext, id);
- if (listener != null) {
- anim.setAnimationListener(listener);
- }
- return anim;
- }
-
- public String viewInfo(View v) {
- return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
- + " " + v.getWidth() + "x" + v.getHeight() + ")";
- }
-
- protected 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 StatusBar from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mQueueLock) {
- pw.println("Current Status Bar state:");
- pw.println(" mExpanded=" + mExpanded
- + ", mExpandedVisible=" + mExpandedVisible);
- pw.println(" mTicking=" + mTicking);
- pw.println(" mTracking=" + mTracking);
- pw.println(" mAnimating=" + mAnimating
- + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
- + ", mAnimAccel=" + mAnimAccel);
- pw.println(" mCurAnimationTime=" + mCurAnimationTime
- + " mAnimLastTime=" + mAnimLastTime);
- pw.println(" mDisplayHeight=" + mDisplayHeight
- + " mAnimatingReveal=" + mAnimatingReveal
- + " mViewDelta=" + mViewDelta);
- pw.println(" mDisplayHeight=" + mDisplayHeight);
- final int N = mQueue.size();
- pw.println(" mQueue.size=" + N);
- for (int i=0; i<N; i++) {
- PendingOp op = mQueue.get(i);
- pw.println(" [" + i + "] key=" + op.key + " code=" + op.code + " visible="
- + op.visible);
- pw.println(" iconData=" + op.iconData);
- pw.println(" notificationData=" + op.notificationData);
- }
- pw.println(" mExpandedParams: " + mExpandedParams);
- pw.println(" mExpandedView: " + viewInfo(mExpandedView));
- pw.println(" mExpandedDialog: " + mExpandedDialog);
- pw.println(" mTrackingParams: " + mTrackingParams);
- pw.println(" mTrackingView: " + viewInfo(mTrackingView));
- pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
- pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
- pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
- pw.println(" mLatestItems: " + viewInfo(mLatestItems));
- pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
- pw.println(" mCloseView: " + viewInfo(mCloseView));
- pw.println(" mTickerView: " + viewInfo(mTickerView));
- pw.println(" mScrollView: " + viewInfo(mScrollView)
- + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
- pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
- }
- synchronized (mIconMap) {
- final int N = mIconMap.size();
- pw.println(" mIconMap.size=" + N);
- Set<IBinder> keys = mIconMap.keySet();
- int i=0;
- for (IBinder key: keys) {
- StatusBarIcon icon = mIconMap.get(key);
- pw.println(" [" + i + "] key=" + key);
- pw.println(" data=" + icon.mData);
- i++;
- }
- }
- synchronized (mNotificationData) {
- int N = mNotificationData.ongoingCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i<N; i++) {
- StatusBarNotification n = mNotificationData.getOngoing(i);
- pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
- pw.println(" data=" + n.data);
- }
- N = mNotificationData.latestCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i<N; i++) {
- StatusBarNotification n = mNotificationData.getLatest(i);
- pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
- pw.println(" data=" + n.data);
- }
- }
- synchronized (mDisableRecords) {
- final int N = mDisableRecords.size();
- pw.println(" mDisableRecords.size=" + N
- + " mDisabled=0x" + Integer.toHexString(mDisabled));
- for (int i=0; i<N; i++) {
- DisableRecord tok = mDisableRecords.get(i);
- pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what)
- + " pkg=" + tok.pkg + " token=" + tok.token);
- }
- }
-
- if (false) {
- pw.println("see the logcat for a dump of the views we have created.");
- // must happen on ui thread
- mHandler.post(new Runnable() {
- public void run() {
- mStatusBarView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mStatusBarView.getWidth() + "x"
- + mStatusBarView.getHeight());
- mStatusBarView.debug();
-
- mExpandedView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mExpandedView.getWidth() + "x"
- + mExpandedView.getHeight());
- mExpandedView.debug();
-
- mTrackingView.getLocationOnScreen(mAbsPos);
- Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
- + ") " + mTrackingView.getWidth() + "x"
- + mTrackingView.getHeight());
- mTrackingView.debug();
- }
- });
- }
- }
-
- void onBarViewAttached() {
- WindowManager.LayoutParams lp;
- int pixelFormat;
- Drawable bg;
-
- /// ---------- Tracking View --------------
- pixelFormat = PixelFormat.RGBX_8888;
- bg = mTrackingView.getBackground();
- if (bg != null) {
- pixelFormat = bg.getOpacity();
- }
-
- lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- pixelFormat);
-// lp.token = mStatusBarView.getWindowToken();
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("TrackingView");
- lp.y = mTrackingPosition;
- mTrackingParams = lp;
-
- WindowManagerImpl.getDefault().addView(mTrackingView, lp);
- }
-
- void onTrackingViewAttached() {
- WindowManager.LayoutParams lp;
- int pixelFormat;
- Drawable bg;
-
- /// ---------- Expanded View --------------
- pixelFormat = PixelFormat.TRANSLUCENT;
-
- final int disph = mDisplay.getHeight();
- lp = mExpandedDialog.getWindow().getAttributes();
- lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
- lp.height = getExpandedHeight();
- lp.x = 0;
- mTrackingPosition = lp.y = -disph; // sufficiently large negative
- lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
- lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_DITHER
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- lp.format = pixelFormat;
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("StatusBarExpanded");
- mExpandedDialog.getWindow().setAttributes(lp);
- mExpandedDialog.getWindow().setFormat(pixelFormat);
- mExpandedParams = lp;
-
- mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
- mExpandedDialog.setContentView(mExpandedView,
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- mExpandedDialog.getWindow().setBackgroundDrawable(null);
- mExpandedDialog.show();
- FrameLayout hack = (FrameLayout)mExpandedView.getParent();
- }
-
- void setDateViewVisibility(boolean visible, int anim) {
- mDateView.setUpdates(visible);
- mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- mDateView.startAnimation(loadAnim(anim, null));
- }
-
- void setNotificationIconVisibility(boolean visible, int anim) {
- int old = mNotificationIcons.getVisibility();
- int v = visible ? View.VISIBLE : View.INVISIBLE;
- if (old != v) {
- mNotificationIcons.setVisibility(v);
- mNotificationIcons.startAnimation(loadAnim(anim, null));
- }
- }
-
- void updateExpandedViewPos(int expandedPosition) {
- if (SPEW) {
- Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
- + " mTrackingParams.y=" + mTrackingParams.y
- + " mTrackingPosition=" + mTrackingPosition);
- }
-
- int h = mStatusBarView.getHeight();
- int disph = mDisplay.getHeight();
-
- // If the expanded view is not visible, make sure they're still off screen.
- // Maybe the view was resized.
- if (!mExpandedVisible) {
- if (mTrackingView != null) {
- mTrackingPosition = -disph;
- if (mTrackingParams != null) {
- mTrackingParams.y = mTrackingPosition;
- WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
- }
- }
- if (mExpandedParams != null) {
- mExpandedParams.y = -disph;
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
- return;
- }
-
- // tracking view...
- int pos;
- if (expandedPosition == EXPANDED_FULL_OPEN) {
- pos = h;
- }
- else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
- pos = mTrackingPosition;
- }
- else {
- if (expandedPosition <= disph) {
- pos = expandedPosition;
- } else {
- pos = disph;
- }
- pos -= disph-h;
- }
- mTrackingPosition = mTrackingParams.y = pos;
- mTrackingParams.height = disph-h;
- WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-
- if (mExpandedParams != null) {
- mCloseView.getLocationInWindow(mPositionTmp);
- final int closePos = mPositionTmp[1];
-
- mExpandedContents.getLocationInWindow(mPositionTmp);
- final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
-
- mExpandedParams.y = pos + mTrackingView.getHeight()
- - (mTrackingParams.height-closePos) - contentsBottom;
- int max = h;
- if (mExpandedParams.y > max) {
- mExpandedParams.y = max;
- }
- int min = mTrackingPosition;
- if (mExpandedParams.y < min) {
- mExpandedParams.y = min;
- }
-
- boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
- if (!visible) {
- // if the contents aren't visible, move the expanded view way off screen
- // because the window itself extends below the content view.
- mExpandedParams.y = -disph;
- }
- panelSlightlyVisible(visible);
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
-
- if (SPEW) {
- Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition
- + " mTrackingParams.y=" + mTrackingParams.y
- + " mTrackingPosition=" + mTrackingPosition
- + " mExpandedParams.y=" + mExpandedParams.y
- + " mExpandedParams.height=" + mExpandedParams.height);
- }
- }
-
- int getExpandedHeight() {
- return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
- }
-
- void updateExpandedHeight() {
- if (mExpandedView != null) {
- mExpandedParams.height = getExpandedHeight();
- mExpandedDialog.getWindow().setAttributes(mExpandedParams);
- }
- }
-
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- private boolean mPanelSlightlyVisible;
- void panelSlightlyVisible(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- if (visible) {
- // tell the notification manager to turn off the lights.
- mNotificationCallbacks.onPanelRevealed();
- }
- }
- }
-
- void performDisableActions(int net) {
- int old = mDisabled;
- int diff = net ^ old;
- mDisabled = net;
-
- // act accordingly
- if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
- if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
- Slog.d(TAG, "DISABLE_EXPAND: yes");
- animateCollapse();
- }
- }
- if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
- if (mTicking) {
- mNotificationIcons.setVisibility(View.INVISIBLE);
- mTicker.halt();
- } else {
- setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
- }
- } else {
- Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
- if (!mExpandedVisible) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- mTicker.halt();
- }
- }
- }
-
- private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
- public void onClick(View v) {
- mNotificationCallbacks.onClearAll();
- addPendingOp(OP_EXPAND, null, false);
- }
- };
-
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)) {
- deactivate();
- }
- else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
- updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
- intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
- intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
- intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
- }
- else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- updateResources();
- }
- }
- };
-
- void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
- if (false) {
- Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn
- + " showPlmn=" + showPlmn + " plmn=" + plmn);
- }
- boolean something = false;
- if (showPlmn) {
- mPlmnLabel.setVisibility(View.VISIBLE);
- if (plmn != null) {
- mPlmnLabel.setText(plmn);
- } else {
- mPlmnLabel.setText(R.string.lockscreen_carrier_default);
- }
- } else {
- mPlmnLabel.setText("");
- mPlmnLabel.setVisibility(View.GONE);
- }
- if (showSpn && spn != null) {
- mSpnLabel.setText(spn);
- mSpnLabel.setVisibility(View.VISIBLE);
- something = true;
- } else {
- mSpnLabel.setText("");
- mSpnLabel.setVisibility(View.GONE);
- }
- }
-
- /**
- * Reload some of our resources when the configuration changes.
- *
- * We don't reload everything when the configuration changes -- we probably
- * should, but getting that smooth is tough. Someday we'll fix that. In the
- * meantime, just update the things that we know change.
- */
- void updateResources() {
- Resources res = mContext.getResources();
-
- mClearButton.setText(mContext.getText(R.string.status_bar_clear_all_button));
- mOngoingTitle.setText(mContext.getText(R.string.status_bar_ongoing_events_title));
- mLatestTitle.setText(mContext.getText(R.string.status_bar_latest_events_title));
- mNoNotificationsTitle.setText(mContext.getText(R.string.status_bar_no_notifications_title));
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- if (false) Slog.v(TAG, "updateResources");
- }
-
- //
- // tracing
- //
-
- void postStartTracing() {
- mHandler.postDelayed(mStartTracing, 3000);
- }
-
- void vibrate() {
- android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
- Context.VIBRATOR_SERVICE);
- vib.vibrate(250);
- }
-
- Runnable mStartTracing = new Runnable() {
- public void run() {
- vibrate();
- SystemClock.sleep(250);
- Slog.d(TAG, "startTracing");
- android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
- mHandler.postDelayed(mStopTracing, 10000);
- }
- };
-
- Runnable mStopTracing = new Runnable() {
- public void run() {
- android.os.Debug.stopMethodTracing();
- Slog.d(TAG, "stopTracing");
- vibrate();
- }
- };
-
- class UninstallReceiver extends BroadcastReceiver {
- public UninstallReceiver() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- filter.addDataScheme("package");
- mContext.registerReceiver(this, filter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(this, sdFilter);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String pkgList[] = null;
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else {
- Uri data = intent.getData();
- if (data != null) {
- String pkg = data.getSchemeSpecificPart();
- if (pkg != null) {
- pkgList = new String[]{pkg};
- }
- }
- }
- ArrayList<StatusBarNotification> list = null;
- if (pkgList != null) {
- synchronized (StatusBarService.this) {
- for (String pkg : pkgList) {
- list = mNotificationData.notificationsForPackage(pkg);
- }
- }
- }
-
- if (list != null) {
- final int N = list.size();
- for (int i=0; i<N; i++) {
- removeIcon(list.get(i).key);
- }
- }
- }
- }
-}
diff --git a/services/java/com/android/server/status/StatusBarView.java b/services/java/com/android/server/status/StatusBarView.java
deleted file mode 100644
index 5e1f572..0000000
--- a/services/java/com/android/server/status/StatusBarView.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Canvas;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-
-public class StatusBarView extends FrameLayout {
- private static final String TAG = "StatusBarView";
-
- static final int DIM_ANIM_TIME = 400;
-
- StatusBarService mService;
- boolean mTracking;
- int mStartX, mStartY;
- ViewGroup mNotificationIcons;
- ViewGroup mStatusIcons;
- View mDate;
- FixedSizeDrawable mBackground;
-
- boolean mNightMode = false;
- int mStartAlpha = 0, mEndAlpha = 0;
- long mEndTime = 0;
-
- public StatusBarView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mNotificationIcons = (ViewGroup)findViewById(R.id.notificationIcons);
- mStatusIcons = (ViewGroup)findViewById(R.id.statusIcons);
- mDate = findViewById(R.id.date);
-
- mBackground = new FixedSizeDrawable(mDate.getBackground());
- mBackground.setFixedBounds(0, 0, 0, 0);
- mDate.setBackgroundDrawable(mBackground);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mService.onBarViewAttached();
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
- if (mNightMode != nightMode) {
- mNightMode = nightMode;
- mStartAlpha = getCurAlpha();
- mEndAlpha = mNightMode ? 0x80 : 0x00;
- mEndTime = SystemClock.uptimeMillis() + DIM_ANIM_TIME;
- invalidate();
- }
- }
-
- int getCurAlpha() {
- long time = SystemClock.uptimeMillis();
- if (time > mEndTime) {
- return mEndAlpha;
- }
- return mEndAlpha
- - (int)(((mEndAlpha-mStartAlpha) * (mEndTime-time) / DIM_ANIM_TIME));
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
-
- // put the date date view quantized to the icons
- int oldDateRight = mDate.getRight();
- int newDateRight;
-
- newDateRight = getDateSize(mNotificationIcons, oldDateRight,
- getViewOffset(mNotificationIcons));
- if (newDateRight < 0) {
- int offset = getViewOffset(mStatusIcons);
- if (oldDateRight < offset) {
- newDateRight = oldDateRight;
- } else {
- newDateRight = getDateSize(mStatusIcons, oldDateRight, offset);
- if (newDateRight < 0) {
- newDateRight = r;
- }
- }
- }
- int max = r - getPaddingRight();
- if (newDateRight > max) {
- newDateRight = max;
- }
-
- mDate.layout(mDate.getLeft(), mDate.getTop(), newDateRight, mDate.getBottom());
- mBackground.setFixedBounds(-mDate.getLeft(), -mDate.getTop(), (r-l), (b-t));
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- int alpha = getCurAlpha();
- if (alpha != 0) {
- canvas.drawARGB(alpha, 0, 0, 0);
- }
- if (alpha != mEndAlpha) {
- invalidate();
- }
- }
-
- /**
- * Gets the left position of v in this view. Throws if v is not
- * a child of this.
- */
- private int getViewOffset(View v) {
- int offset = 0;
- while (v != this) {
- offset += v.getLeft();
- ViewParent p = v.getParent();
- if (v instanceof View) {
- v = (View)p;
- } else {
- throw new RuntimeException(v + " is not a child of " + this);
- }
- }
- return offset;
- }
-
- private int getDateSize(ViewGroup g, int w, int offset) {
- final int N = g.getChildCount();
- for (int i=0; i<N; i++) {
- View v = g.getChildAt(i);
- int l = v.getLeft() + offset;
- int r = v.getRight() + offset;
- if (w >= l && w <= r) {
- return r;
- }
- }
- return -1;
- }
-
- /**
- * Ensure that, if there is no target under us to receive the touch,
- * that we process it ourself. This makes sure that onInterceptTouchEvent()
- * is always called for the entire gesture.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() != MotionEvent.ACTION_DOWN) {
- mService.interceptTouchEvent(event);
- }
- return true;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return mService.interceptTouchEvent(event)
- ? true : super.onInterceptTouchEvent(event);
- }
-}
-
diff --git a/services/java/com/android/server/status/StorageNotification.java b/services/java/com/android/server/status/StorageNotification.java
deleted file mode 100644
index 8da8cd3..0000000
--- a/services/java/com/android/server/status/StorageNotification.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.status;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.storage.IMountService;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.storage.StorageEventListener;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageResultCode;
-import android.provider.Settings;
-import android.util.Slog;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class StorageNotification extends StorageEventListener {
- private static final String TAG = "StorageNotification";
-
- private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true;
-
- /**
- * Binder context for this service
- */
- private Context mContext;
-
- /**
- * The notification that is shown when a USB mass storage host
- * is connected.
- * <p>
- * This is lazily created, so use {@link #setUsbStorageNotification()}.
- */
- private Notification mUsbStorageNotification;
-
- /**
- * The notification that is shown when the following media events occur:
- * - Media is being checked
- * - Media is blank (or unknown filesystem)
- * - Media is corrupt
- * - Media is safe to unmount
- * - Media is missing
- * <p>
- * This is lazily created, so use {@link #setMediaStorageNotification()}.
- */
- private Notification mMediaStorageNotification;
- private boolean mUmsAvailable;
- private StorageManager mStorageManager;
-
- public StorageNotification(Context context) {
- mContext = context;
-
- mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
- mUmsAvailable = mStorageManager.isUsbMassStorageConnected();
- Slog.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable,
- Environment.getExternalStorageState()));
- }
-
- /*
- * @override com.android.os.storage.StorageEventListener
- */
- @Override
- public void onUsbMassStorageConnectionChanged(boolean connected) {
- mUmsAvailable = connected;
- /*
- * Even though we may have a UMS host connected, we the SD card
- * may not be in a state for export.
- */
- String st = Environment.getExternalStorageState();
-
- Slog.i(TAG, String.format("UMS connection changed to %s (media state %s)", connected, st));
-
- if (connected && (st.equals(
- Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) {
- /*
- * No card or card being checked = don't display
- */
- connected = false;
- }
- updateUsbMassStorageNotification(connected);
- }
-
- /*
- * @override com.android.os.storage.StorageEventListener
- */
- @Override
- public void onStorageStateChanged(String path, String oldState, String newState) {
- Slog.i(TAG, String.format(
- "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
- if (newState.equals(Environment.MEDIA_SHARED)) {
- /*
- * Storage is now shared. Modify the UMS notification
- * for stopping UMS.
- */
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
- setUsbStorageNotification(
- com.android.internal.R.string.usb_storage_stop_notification_title,
- com.android.internal.R.string.usb_storage_stop_notification_message,
- com.android.internal.R.drawable.stat_sys_warning, false, true, pi);
- } else if (newState.equals(Environment.MEDIA_CHECKING)) {
- /*
- * Storage is now checking. Update media notification and disable
- * UMS notification.
- */
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_checking_notification_title,
- com.android.internal.R.string.ext_media_checking_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null);
- updateUsbMassStorageNotification(false);
- } else if (newState.equals(Environment.MEDIA_MOUNTED)) {
- /*
- * Storage is now mounted. Dismiss any media notifications,
- * and enable UMS notification if connected.
- */
- setMediaStorageNotification(0, 0, 0, false, false, null);
- updateUsbMassStorageNotification(mUmsAvailable);
- } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) {
- /*
- * Storage is now unmounted. We may have been unmounted
- * because the user is enabling/disabling UMS, in which case we don't
- * want to display the 'safe to unmount' notification.
- */
- if (!mStorageManager.isUsbMassStorageEnabled()) {
- if (oldState.equals(Environment.MEDIA_SHARED)) {
- /*
- * The unmount was due to UMS being enabled. Dismiss any
- * media notifications, and enable UMS notification if connected
- */
- setMediaStorageNotification(0, 0, 0, false, false, null);
- updateUsbMassStorageNotification(mUmsAvailable);
- } else {
- /*
- * Show safe to unmount media notification, and enable UMS
- * notification if connected.
- */
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_safe_unmount_notification_title,
- com.android.internal.R.string.ext_media_safe_unmount_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
- updateUsbMassStorageNotification(mUmsAvailable);
- }
- } else {
- /*
- * The unmount was due to UMS being enabled. Dismiss any
- * media notifications, and disable the UMS notification
- */
- setMediaStorageNotification(0, 0, 0, false, false, null);
- updateUsbMassStorageNotification(false);
- }
- } else if (newState.equals(Environment.MEDIA_NOFS)) {
- /*
- * Storage has no filesystem. Show blank media notification,
- * and enable UMS notification if connected.
- */
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_nofs_notification_title,
- com.android.internal.R.string.ext_media_nofs_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
- updateUsbMassStorageNotification(mUmsAvailable);
- } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) {
- /*
- * Storage is corrupt. Show corrupt media notification,
- * and enable UMS notification if connected.
- */
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_unmountable_notification_title,
- com.android.internal.R.string.ext_media_unmountable_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
- updateUsbMassStorageNotification(mUmsAvailable);
- } else if (newState.equals(Environment.MEDIA_REMOVED)) {
- /*
- * Storage has been removed. Show nomedia media notification,
- * and disable UMS notification regardless of connection state.
- */
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_nomedia_notification_title,
- com.android.internal.R.string.ext_media_nomedia_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_usb,
- true, false, null);
- updateUsbMassStorageNotification(false);
- } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) {
- /*
- * Storage has been removed unsafely. Show bad removal media notification,
- * and disable UMS notification regardless of connection state.
- */
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_badremoval_notification_title,
- com.android.internal.R.string.ext_media_badremoval_notification_message,
- com.android.internal.R.drawable.stat_sys_warning,
- true, true, null);
- updateUsbMassStorageNotification(false);
- } else {
- Slog.w(TAG, String.format("Ignoring unknown state {%s}", newState));
- }
- }
-
- /**
- * Update the state of the USB mass storage notification
- */
- void updateUsbMassStorageNotification(boolean available) {
-
- if (available) {
- Intent intent = new Intent();
- intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- final boolean adbOn = 1 == Settings.Secure.getInt(
- mContext.getContentResolver(),
- Settings.Secure.ADB_ENABLED,
- 0);
-
- PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
- setUsbStorageNotification(
- com.android.internal.R.string.usb_storage_notification_title,
- com.android.internal.R.string.usb_storage_notification_message,
- com.android.internal.R.drawable.stat_sys_data_usb,
- false, true, pi);
-
- if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
- // We assume that developers don't want to enable UMS every
- // time they attach a device to a USB host. The average user,
- // however, is looking to charge the phone (in which case this
- // is harmless) or transfer files (in which case this coaches
- // the user about how to complete that task and saves several
- // steps).
- mContext.startActivity(intent);
- }
- } else {
- setUsbStorageNotification(0, 0, 0, false, false, null);
- }
- }
-
- /**
- * Sets the USB storage notification.
- */
- private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon,
- boolean sound, boolean visible, PendingIntent pi) {
-
- if (!visible && mUsbStorageNotification == null) {
- return;
- }
-
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (notificationManager == null) {
- return;
- }
-
- if (visible) {
- Resources r = Resources.getSystem();
- CharSequence title = r.getText(titleId);
- CharSequence message = r.getText(messageId);
-
- if (mUsbStorageNotification == null) {
- mUsbStorageNotification = new Notification();
- mUsbStorageNotification.icon = icon;
- mUsbStorageNotification.when = 0;
- }
-
- if (sound) {
- mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
- } else {
- mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
- }
-
- mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
-
- mUsbStorageNotification.tickerText = title;
- if (pi == null) {
- Intent intent = new Intent();
- pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- }
-
- mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
- }
-
- final int notificationId = mUsbStorageNotification.icon;
- if (visible) {
- notificationManager.notify(notificationId, mUsbStorageNotification);
- } else {
- notificationManager.cancel(notificationId);
- }
- }
-
- private synchronized boolean getMediaStorageNotificationDismissable() {
- if ((mMediaStorageNotification != null) &&
- ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
- Notification.FLAG_AUTO_CANCEL))
- return true;
-
- return false;
- }
-
- /**
- * Sets the media storage notification.
- */
- private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
- boolean dismissable, PendingIntent pi) {
-
- if (!visible && mMediaStorageNotification == null) {
- return;
- }
-
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (notificationManager == null) {
- return;
- }
-
- if (mMediaStorageNotification != null && visible) {
- /*
- * Dismiss the previous notification - we're about to
- * re-use it.
- */
- final int notificationId = mMediaStorageNotification.icon;
- notificationManager.cancel(notificationId);
- }
-
- if (visible) {
- Resources r = Resources.getSystem();
- CharSequence title = r.getText(titleId);
- CharSequence message = r.getText(messageId);
-
- if (mMediaStorageNotification == null) {
- mMediaStorageNotification = new Notification();
- mMediaStorageNotification.when = 0;
- }
-
- mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
-
- if (dismissable) {
- mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
- } else {
- mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
- }
-
- mMediaStorageNotification.tickerText = title;
- if (pi == null) {
- Intent intent = new Intent();
- pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- }
-
- mMediaStorageNotification.icon = icon;
- mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
- }
-
- final int notificationId = mMediaStorageNotification.icon;
- if (visible) {
- notificationManager.notify(notificationId, mMediaStorageNotification);
- } else {
- notificationManager.cancel(notificationId);
- }
- }
-}
diff --git a/services/java/com/android/server/status/Ticker.java b/services/java/com/android/server/status/Ticker.java
deleted file mode 100644
index e47185b..0000000
--- a/services/java/com/android/server/status/Ticker.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import com.android.internal.R;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.text.StaticLayout;
-import android.text.Layout.Alignment;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.TextSwitcher;
-import android.widget.TextView;
-import android.widget.ImageSwitcher;
-
-import java.util.ArrayList;
-
-
-abstract class Ticker {
- private static final int TICKER_SEGMENT_DELAY = 3000;
-
- private final class Segment {
- NotificationData notificationData;
- Drawable icon;
- CharSequence text;
- int current;
- int next;
- boolean first;
-
- StaticLayout getLayout(CharSequence substr) {
- int w = mTextSwitcher.getWidth() - mTextSwitcher.getPaddingLeft()
- - mTextSwitcher.getPaddingRight();
- return new StaticLayout(substr, mPaint, w, Alignment.ALIGN_NORMAL, 1, 0, true);
- }
-
- CharSequence rtrim(CharSequence substr, int start, int end) {
- while (end > start && !TextUtils.isGraphic(substr.charAt(end-1))) {
- end--;
- }
- if (end > start) {
- return substr.subSequence(start, end);
- }
- return null;
- }
-
- /** returns null if there is no more text */
- CharSequence getText() {
- if (this.current > this.text.length()) {
- return null;
- }
- CharSequence substr = this.text.subSequence(this.current, this.text.length());
- StaticLayout l = getLayout(substr);
- int lineCount = l.getLineCount();
- if (lineCount > 0) {
- int start = l.getLineStart(0);
- int end = l.getLineEnd(0);
- this.next = this.current + end;
- return rtrim(substr, start, end);
- } else {
- throw new RuntimeException("lineCount=" + lineCount + " current=" + current +
- " text=" + text);
- }
- }
-
- /** returns null if there is no more text */
- CharSequence advance() {
- this.first = false;
- int index = this.next;
- final int len = this.text.length();
- while (index < len && !TextUtils.isGraphic(this.text.charAt(index))) {
- index++;
- }
- if (index >= len) {
- return null;
- }
-
- CharSequence substr = this.text.subSequence(index, this.text.length());
- StaticLayout l = getLayout(substr);
- final int lineCount = l.getLineCount();
- int i;
- for (i=0; i<lineCount; i++) {
- int start = l.getLineStart(i);
- int end = l.getLineEnd(i);
- if (i == lineCount-1) {
- this.next = len;
- } else {
- this.next = index + l.getLineStart(i+1);
- }
- CharSequence result = rtrim(substr, start, end);
- if (result != null) {
- this.current = index + start;
- return result;
- }
- }
- this.current = len;
- return null;
- }
-
- Segment(NotificationData n, Drawable icon, CharSequence text) {
- this.notificationData = n;
- this.icon = icon;
- this.text = text;
- int index = 0;
- final int len = text.length();
- while (index < len && !TextUtils.isGraphic(text.charAt(index))) {
- index++;
- }
- this.current = index;
- this.next = index;
- this.first = true;
- }
- };
-
- private Handler mHandler = new Handler();
- private ArrayList<Segment> mSegments = new ArrayList();
- private TextPaint mPaint;
- private View mTickerView;
- private ImageSwitcher mIconSwitcher;
- private TextSwitcher mTextSwitcher;
-
- Ticker(Context context, StatusBarView sb) {
- mTickerView = sb.findViewById(R.id.ticker);
-
- mIconSwitcher = (ImageSwitcher)sb.findViewById(R.id.tickerIcon);
- mIconSwitcher.setInAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
- mIconSwitcher.setOutAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
-
- mTextSwitcher = (TextSwitcher)sb.findViewById(R.id.tickerText);
- mTextSwitcher.setInAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
- mTextSwitcher.setOutAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
-
- // Copy the paint style of one of the TextSwitchers children to use later for measuring
- TextView text = (TextView)mTextSwitcher.getChildAt(0);
- mPaint = text.getPaint();
- }
-
- void addEntry(NotificationData n, Drawable icon, CharSequence text) {
- int initialCount = mSegments.size();
-
- Segment newSegment = new Segment(n, icon, text);
-
- // prune out any preexisting ones for this notification, but not the current one.
- // let that finish, even if it's the same id
- for (int i=1; i<initialCount; i++) {
- Segment seg = mSegments.get(i);
- if (n.id == seg.notificationData.id && n.pkg.equals(seg.notificationData.pkg)) {
- // just update that one to use this new data instead
- mSegments.set(i, newSegment);
- // and since we know initialCount != 0, just return
- return ;
- }
- }
-
- mSegments.add(newSegment);
-
- if (initialCount == 0 && mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
- seg.first = false;
-
- mIconSwitcher.setAnimateFirstView(false);
- mIconSwitcher.reset();
- mIconSwitcher.setImageDrawable(seg.icon);
-
- mTextSwitcher.setAnimateFirstView(false);
- mTextSwitcher.reset();
- mTextSwitcher.setText(seg.getText());
-
- tickerStarting();
- scheduleAdvance();
- }
- }
-
- void halt() {
- mHandler.removeCallbacks(mAdvanceTicker);
- mSegments.clear();
- tickerHalting();
- }
-
- void reflowText() {
- if (mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
- CharSequence text = seg.getText();
- mTextSwitcher.setCurrentText(text);
- }
- }
-
- private Runnable mAdvanceTicker = new Runnable() {
- public void run() {
- while (mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
-
- if (seg.first) {
- // this makes the icon slide in for the first one for a given
- // notification even if there are two notifications with the
- // same icon in a row
- mIconSwitcher.setImageDrawable(seg.icon);
- }
- CharSequence text = seg.advance();
- if (text == null) {
- mSegments.remove(0);
- continue;
- }
- mTextSwitcher.setText(text);
-
- scheduleAdvance();
- break;
- }
- if (mSegments.size() == 0) {
- tickerDone();
- }
- }
- };
-
- private void scheduleAdvance() {
- mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
- }
-
- abstract void tickerStarting();
- abstract void tickerDone();
- abstract void tickerHalting();
-}
-
diff --git a/services/java/com/android/server/status/TickerView.java b/services/java/com/android/server/status/TickerView.java
deleted file mode 100644
index 099dffb..0000000
--- a/services/java/com/android/server/status/TickerView.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.TextSwitcher;
-
-
-public class TickerView extends TextSwitcher
-{
- Ticker mTicker;
-
- public TickerView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mTicker.reflowText();
- }
-}
-
diff --git a/services/java/com/android/server/status/TrackingPatternView.java b/services/java/com/android/server/status/TrackingPatternView.java
deleted file mode 100644
index 2c91aa4..0000000
--- a/services/java/com/android/server/status/TrackingPatternView.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.View;
-import android.graphics.BitmapFactory;
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.graphics.Canvas;
-
-public class TrackingPatternView extends View {
- private Bitmap mTexture;
- private Paint mPaint;
- private int mTextureWidth;
- private int mTextureHeight;
-
- public TrackingPatternView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mTexture = BitmapFactory.decodeResource(getResources(),
- com.android.internal.R.drawable.status_bar_background);
- mTextureWidth = mTexture.getWidth();
- mTextureHeight = mTexture.getHeight();
-
- mPaint = new Paint();
- mPaint.setDither(false);
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- final Bitmap texture = mTexture;
- final Paint paint = mPaint;
-
- final int width = getWidth();
- final int height = getHeight();
-
- final int textureWidth = mTextureWidth;
- final int textureHeight = mTextureHeight;
-
- int x = 0;
- int y;
-
- while (x < width) {
- y = 0;
- while (y < height) {
- canvas.drawBitmap(texture, x, y, paint);
- y += textureHeight;
- }
- x += textureWidth;
- }
- }
-}
diff --git a/services/java/com/android/server/status/TrackingView.java b/services/java/com/android/server/status/TrackingView.java
deleted file mode 100644
index 8ec39c0..0000000
--- a/services/java/com/android/server/status/TrackingView.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2008 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.status;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.KeyEvent;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-
-
-public class TrackingView extends LinearLayout {
- final Display mDisplay;
- StatusBarService mService;
- boolean mTracking;
- int mStartX, mStartY;
-
- public TrackingView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mDisplay = ((WindowManager)context.getSystemService(
- Context.WINDOW_SERVICE)).getDefaultDisplay();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mService.updateExpandedHeight();
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (down) {
- mService.deactivate();
- }
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mService.onTrackingViewAttached();
- }
-}
diff --git a/services/java/com/android/server/status/UsbStorageActivity.java b/services/java/com/android/server/status/UsbStorageActivity.java
deleted file mode 100644
index e8631c5..0000000
--- a/services/java/com/android/server/status/UsbStorageActivity.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2007 Google Inc.
- *
- * 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.status;
-
-import com.android.internal.R;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.storage.IMountService;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageEventListener;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.widget.ImageView;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.view.View;
-import android.view.Window;
-import android.util.Log;
-
-import java.util.List;
-
-/**
- * This activity is shown to the user for him/her to enable USB mass storage
- * on-demand (that is, when the USB cable is connected). It uses the alert
- * dialog style. It will be launched from a notification.
- */
-public class UsbStorageActivity extends Activity
- implements View.OnClickListener, OnCancelListener {
- private static final String TAG = "UsbStorageActivity";
-
- private Button mMountButton;
- private Button mUnmountButton;
- private ProgressBar mProgressBar;
- private TextView mBanner;
- private TextView mMessage;
- private ImageView mIcon;
- private StorageManager mStorageManager = null;
- private static final int DLG_CONFIRM_KILL_STORAGE_USERS = 1;
- private static final int DLG_ERROR_SHARING = 2;
- static final boolean localLOGV = false;
-
- /** Used to detect when the USB cable is unplugged, so we can call finish() */
- private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) {
- handleBatteryChanged(intent);
- }
- }
- };
-
- private StorageEventListener mStorageListener = new StorageEventListener() {
- @Override
- public void onStorageStateChanged(String path, String oldState, String newState) {
- final boolean on = newState.equals(Environment.MEDIA_SHARED);
- switchDisplay(on);
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (mStorageManager == null) {
- mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
- if (mStorageManager == null) {
- Log.w(TAG, "Failed to get StorageManager");
- }
- }
-
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setProgressBarIndeterminateVisibility(true);
-
- setTitle(getString(com.android.internal.R.string.usb_storage_activity_title));
-
- setContentView(com.android.internal.R.layout.usb_storage_activity);
-
- mIcon = (ImageView) findViewById(com.android.internal.R.id.icon);
- mBanner = (TextView) findViewById(com.android.internal.R.id.banner);
- mMessage = (TextView) findViewById(com.android.internal.R.id.message);
-
- mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button);
- mMountButton.setOnClickListener(this);
- mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button);
- mUnmountButton.setOnClickListener(this);
- mProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress);
- }
-
- private void switchDisplay(boolean usbStorageInUse) {
- if (usbStorageInUse) {
- mProgressBar.setVisibility(View.GONE);
- mUnmountButton.setVisibility(View.VISIBLE);
- mMountButton.setVisibility(View.GONE);
- mIcon.setImageResource(com.android.internal.R.drawable.usb_android_connected);
- mBanner.setText(com.android.internal.R.string.usb_storage_stop_title);
- mMessage.setText(com.android.internal.R.string.usb_storage_stop_message);
- } else {
- mProgressBar.setVisibility(View.GONE);
- mUnmountButton.setVisibility(View.GONE);
- mMountButton.setVisibility(View.VISIBLE);
- mIcon.setImageResource(com.android.internal.R.drawable.usb_android);
- mBanner.setText(com.android.internal.R.string.usb_storage_title);
- mMessage.setText(com.android.internal.R.string.usb_storage_message);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- mStorageManager.registerListener(mStorageListener);
- registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
- try {
- switchDisplay(mStorageManager.isUsbMassStorageEnabled());
- } catch (Exception ex) {
- Log.e(TAG, "Failed to read UMS enable state", ex);
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- unregisterReceiver(mBatteryReceiver);
- if (mStorageManager == null && mStorageListener != null) {
- mStorageManager.unregisterListener(mStorageListener);
- }
- }
-
- private void handleBatteryChanged(Intent intent) {
- int pluggedType = intent.getIntExtra("plugged", 0);
- if (pluggedType == 0) {
- // It was disconnected from the plug, so finish
- finish();
- }
- }
-
- private IMountService getMountService() {
- IBinder service = ServiceManager.getService("mount");
- if (service != null) {
- return IMountService.Stub.asInterface(service);
- }
- return null;
- }
-
- @Override
- public Dialog onCreateDialog(int id, Bundle args) {
- switch (id) {
- case DLG_CONFIRM_KILL_STORAGE_USERS:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.dlg_confirm_kill_storage_users_title)
- .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- switchUsbMassStorageAsync(true);
- }})
- .setNegativeButton(R.string.cancel, null)
- .setMessage(R.string.dlg_confirm_kill_storage_users_text)
- .setOnCancelListener(this)
- .create();
- case DLG_ERROR_SHARING:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.dlg_error_title)
- .setNeutralButton(R.string.dlg_ok, null)
- .setMessage(R.string.usb_storage_error_message)
- .setOnCancelListener(this)
- .create();
- }
- return null;
- }
-
- private void showDialogInner(int id) {
- removeDialog(id);
- showDialog(id);
- }
-
- private void switchUsbMassStorageAsync(boolean on) {
- mUnmountButton.setVisibility(View.GONE);
- mMountButton.setVisibility(View.GONE);
-
- mProgressBar.setVisibility(View.VISIBLE);
- // will be hidden once USB mass storage kicks in (or fails)
-
- final boolean _on = on;
- new Thread() {
- public void run() {
- if (_on) {
- mStorageManager.enableUsbMassStorage();
- } else {
- mStorageManager.disableUsbMassStorage();
- }
- }
- }.start();
- }
-
- private void checkStorageUsers() {
- IMountService ims = getMountService();
- if (ims == null) {
- // Display error dialog
- showDialogInner(DLG_ERROR_SHARING);
- }
- String extStoragePath = Environment.getExternalStorageDirectory().toString();
- boolean showDialog = false;
- try {
- int[] stUsers = ims.getStorageUsers(extStoragePath);
- if (stUsers != null && stUsers.length > 0) {
- showDialog = true;
- } else {
- // List of applications on sdcard.
- ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- List<ApplicationInfo> infoList = am.getRunningExternalApplications();
- if (infoList != null && infoList.size() > 0) {
- showDialog = true;
- }
- }
- } catch (RemoteException e) {
- // Display error dialog
- showDialogInner(DLG_ERROR_SHARING);
- }
- if (showDialog) {
- // Display dialog to user
- showDialogInner(DLG_CONFIRM_KILL_STORAGE_USERS);
- } else {
- if (localLOGV) Log.i(TAG, "Enabling UMS");
- switchUsbMassStorageAsync(true);
- }
- }
-
- public void onClick(View v) {
- if (v == mMountButton) {
- // Check for list of storage users and display dialog if needed.
- checkStorageUsers();
- } else if (v == mUnmountButton) {
- if (localLOGV) Log.i(TAG, "Disabling UMS");
- switchUsbMassStorageAsync(false);
- }
- }
-
- public void onCancel(DialogInterface dialog) {
- finish();
- }
-
-}
diff --git a/services/java/com/android/server/status/package.html b/services/java/com/android/server/status/package.html
deleted file mode 100755
index c9f96a6..0000000
--- a/services/java/com/android/server/status/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 9d2760e..cdc0a6f 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -4,17 +4,19 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
com_android_server_BatteryService.cpp \
- com_android_server_KeyInputQueue.cpp \
+ com_android_server_InputManager.cpp \
com_android_server_LightsService.cpp \
- com_android_server_SensorService.cpp \
+ com_android_server_PowerManagerService.cpp \
com_android_server_SystemServer.cpp \
com_android_server_VibratorService.cpp \
+ com_android_server_location_GpsLocationProvider.cpp \
onload.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
libcutils \
libhardware \
libhardware_legacy \
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
new file mode 100644
index 0000000..a237ee9
--- /dev/null
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -0,0 +1,2590 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputManager-JNI"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about InputReaderPolicy
+#define DEBUG_INPUT_READER_POLICY 0
+
+// Log debug messages about InputDispatcherPolicy
+#define DEBUG_INPUT_DISPATCHER_POLICY 0
+
+// Log debug messages about input focus tracking
+#define DEBUG_FOCUS 0
+
+#include "JNIHelp.h"
+#include "jni.h"
+#include <limits.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <ui/InputReader.h>
+#include <ui/InputDispatcher.h>
+#include <ui/InputManager.h>
+#include <ui/InputTransport.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include "../../core/jni/android_view_KeyEvent.h"
+#include "../../core/jni/android_view_MotionEvent.h"
+#include "../../core/jni/android_view_InputChannel.h"
+#include "com_android_server_PowerManagerService.h"
+
+namespace android {
+
+// Window flags from WindowManager.LayoutParams
+enum {
+ FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ FLAG_DIM_BEHIND = 0x00000002,
+ FLAG_BLUR_BEHIND = 0x00000004,
+ FLAG_NOT_FOCUSABLE = 0x00000008,
+ FLAG_NOT_TOUCHABLE = 0x00000010,
+ FLAG_NOT_TOUCH_MODAL = 0x00000020,
+ FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+ FLAG_KEEP_SCREEN_ON = 0x00000080,
+ FLAG_LAYOUT_IN_SCREEN = 0x00000100,
+ FLAG_LAYOUT_NO_LIMITS = 0x00000200,
+ FLAG_FULLSCREEN = 0x00000400,
+ FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
+ FLAG_DITHER = 0x00001000,
+ FLAG_SECURE = 0x00002000,
+ FLAG_SCALED = 0x00004000,
+ FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
+ FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+ FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+ FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+ FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+ FLAG_SHOW_WALLPAPER = 0x00100000,
+ FLAG_TURN_SCREEN_ON = 0x00200000,
+ FLAG_DISMISS_KEYGUARD = 0x00400000,
+ FLAG_IMMERSIVE = 0x00800000,
+ FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
+ FLAG_COMPATIBLE_WINDOW = 0x20000000,
+ FLAG_SYSTEM_ERROR = 0x40000000,
+};
+
+// Window types from WindowManager.LayoutParams
+enum {
+ FIRST_APPLICATION_WINDOW = 1,
+ TYPE_BASE_APPLICATION = 1,
+ TYPE_APPLICATION = 2,
+ TYPE_APPLICATION_STARTING = 3,
+ LAST_APPLICATION_WINDOW = 99,
+ FIRST_SUB_WINDOW = 1000,
+ TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
+ TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1,
+ TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
+ TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
+ TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4,
+ LAST_SUB_WINDOW = 1999,
+ FIRST_SYSTEM_WINDOW = 2000,
+ TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
+ TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1,
+ TYPE_PHONE = FIRST_SYSTEM_WINDOW+2,
+ TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3,
+ TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4,
+ TYPE_TOAST = FIRST_SYSTEM_WINDOW+5,
+ TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6,
+ TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7,
+ TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8,
+ TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9,
+ TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10,
+ TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
+ TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
+ TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
+ TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
+ LAST_SYSTEM_WINDOW = 2999,
+};
+
+// Delay between reporting long touch events to the power manager.
+const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Minimum amount of time to provide to the input dispatcher for delivery of an event
+// regardless of how long the application window was paused.
+const nsecs_t MIN_INPUT_DISPATCHING_TIMEOUT = 1000 * 1000000LL; // 1 sec
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+
+ jmethodID notifyConfigurationChanged;
+ jmethodID notifyLidSwitchChanged;
+ jmethodID notifyInputChannelBroken;
+ jmethodID notifyInputChannelANR;
+ jmethodID notifyInputChannelRecoveredFromANR;
+ jmethodID notifyANR;
+ jmethodID virtualKeyDownFeedback;
+ jmethodID interceptKeyBeforeQueueing;
+ jmethodID interceptKeyBeforeDispatching;
+ jmethodID checkInjectEventsPermission;
+ jmethodID notifyAppSwitchComing;
+ jmethodID filterTouchEvents;
+ jmethodID filterJumpyTouchEvents;
+ jmethodID getVirtualKeyDefinitions;
+ jmethodID getInputDeviceCalibration;
+ jmethodID getExcludedDeviceNames;
+ jmethodID getMaxEventsPerSecond;
+} gCallbacksClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jfieldID scanCode;
+ jfieldID centerX;
+ jfieldID centerY;
+ jfieldID width;
+ jfieldID height;
+} gVirtualKeyDefinitionClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jfieldID keys;
+ jfieldID values;
+} gInputDeviceCalibrationClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jfieldID inputChannel;
+ jfieldID layoutParamsFlags;
+ jfieldID layoutParamsType;
+ jfieldID dispatchingTimeoutNanos;
+ jfieldID frameLeft;
+ jfieldID frameTop;
+ jfieldID touchableAreaLeft;
+ jfieldID touchableAreaTop;
+ jfieldID touchableAreaRight;
+ jfieldID touchableAreaBottom;
+ jfieldID visible;
+ jfieldID hasFocus;
+ jfieldID hasWallpaper;
+ jfieldID paused;
+ jfieldID ownerPid;
+ jfieldID ownerUid;
+} gInputWindowClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jfieldID name;
+ jfieldID dispatchingTimeoutNanos;
+ jfieldID token;
+} gInputApplicationClassInfo;
+
+static struct {
+ jclass clazz;
+} gKeyEventClassInfo;
+
+static struct {
+ jclass clazz;
+} gMotionEventClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jmethodID ctor;
+ jmethodID addMotionRange;
+
+ jfieldID mId;
+ jfieldID mName;
+ jfieldID mSources;
+ jfieldID mKeyboardType;
+ jfieldID mMotionRanges;
+} gInputDeviceClassInfo;
+
+// ----------------------------------------------------------------------------
+
+static inline nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+// ----------------------------------------------------------------------------
+
+class NativeInputManager : public virtual RefBase,
+ public virtual InputReaderPolicyInterface,
+ public virtual InputDispatcherPolicyInterface {
+protected:
+ virtual ~NativeInputManager();
+
+public:
+ NativeInputManager(jobject callbacksObj);
+
+ inline sp<InputManager> getInputManager() const { return mInputManager; }
+
+ String8 dump();
+
+ void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
+ void setDisplayOrientation(int32_t displayId, int32_t orientation);
+
+ status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
+ jweak inputChannelObjWeak, bool monitor);
+ status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
+
+ void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
+ void setFocusedApplication(JNIEnv* env, jobject applicationObj);
+ void setInputDispatchMode(bool enabled, bool frozen);
+ void preemptInputDispatch();
+
+ /* --- InputReaderPolicyInterface implementation --- */
+
+ virtual bool getDisplayInfo(int32_t displayId,
+ int32_t* width, int32_t* height, int32_t* orientation);
+ virtual void virtualKeyDownFeedback();
+ virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
+ bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags);
+ virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
+ uint32_t& policyFlags);
+ virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags);
+ virtual bool filterTouchEvents();
+ virtual bool filterJumpyTouchEvents();
+ virtual void getVirtualKeyDefinitions(const String8& deviceName,
+ Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions);
+ virtual void getInputDeviceCalibration(const String8& deviceName,
+ InputDeviceCalibration& outCalibration);
+ virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
+
+ /* --- InputDispatcherPolicyInterface implementation --- */
+
+ virtual void notifyConfigurationChanged(nsecs_t when);
+ virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
+ virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+ nsecs_t& outNewTimeout);
+ virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
+ virtual nsecs_t getKeyRepeatTimeout();
+ virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+ virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+ virtual int32_t getMaxEventsPerSecond();
+
+private:
+ struct InputWindow {
+ sp<InputChannel> inputChannel;
+ int32_t layoutParamsFlags;
+ int32_t layoutParamsType;
+ nsecs_t dispatchingTimeout;
+ int32_t frameLeft;
+ int32_t frameTop;
+ int32_t touchableAreaLeft;
+ int32_t touchableAreaTop;
+ int32_t touchableAreaRight;
+ int32_t touchableAreaBottom;
+ bool visible;
+ bool hasFocus;
+ bool hasWallpaper;
+ bool paused;
+ int32_t ownerPid;
+ int32_t ownerUid;
+
+ inline bool touchableAreaContainsPoint(int32_t x, int32_t y) {
+ return x >= touchableAreaLeft && x <= touchableAreaRight
+ && y >= touchableAreaTop && y <= touchableAreaBottom;
+ }
+ };
+
+ struct InputApplication {
+ String8 name;
+ nsecs_t dispatchingTimeout;
+ jweak tokenObjWeak;
+ };
+
+ class ANRTimer {
+ enum Budget {
+ SYSTEM = 0,
+ APPLICATION = 1
+ };
+
+ Budget mBudget;
+ nsecs_t mStartTime;
+ bool mFrozen;
+ InputWindow* mPausedWindow;
+
+ public:
+ ANRTimer();
+
+ void dispatchFrozenBySystem();
+ void dispatchPausedByApplication(InputWindow* pausedWindow);
+ bool waitForDispatchStateChangeLd(NativeInputManager* inputManager);
+
+ nsecs_t getTimeSpentWaitingForApplication() const;
+ };
+
+ sp<InputManager> mInputManager;
+
+ jobject mCallbacksObj;
+
+ // Cached filtering policies.
+ int32_t mFilterTouchEvents;
+ int32_t mFilterJumpyTouchEvents;
+
+ // Cached throttling policy.
+ int32_t mMaxEventsPerSecond;
+
+ // Cached display state. (lock mDisplayLock)
+ Mutex mDisplayLock;
+ int32_t mDisplayWidth, mDisplayHeight;
+ int32_t mDisplayOrientation;
+
+ // Power manager interactions.
+ bool isScreenOn();
+ bool isScreenBright();
+
+ // Weak references to all currently registered input channels by connection pointer.
+ Mutex mInputChannelRegistryLock;
+ KeyedVector<InputChannel*, jweak> mInputChannelObjWeakTable;
+
+ jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
+
+ // Input target and focus tracking. (lock mDispatchLock)
+ Mutex mDispatchLock;
+ Condition mDispatchStateChanged;
+
+ bool mDispatchEnabled;
+ bool mDispatchFrozen;
+ bool mWindowsReady;
+ Vector<InputWindow> mWindows;
+ Vector<InputWindow*> mWallpaperWindows;
+ Vector<sp<InputChannel> > mMonitoringChannels;
+
+ // Focus tracking for keys, trackball, etc.
+ InputWindow* mFocusedWindow;
+
+ // Focus tracking for touch.
+ bool mTouchDown;
+ InputWindow* mTouchedWindow; // primary target for current down
+ Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
+
+ Vector<InputWindow*> mTempTouchedOutsideWindows; // temporary outside touch targets
+ Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+
+ // Focused application.
+ InputApplication* mFocusedApplication;
+ InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+
+ void dumpDeviceInfo(String8& dump);
+ void dumpDispatchStateLd(String8& dump);
+ void logDispatchStateLd();
+
+ bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout);
+ void releaseFocusedApplicationLd(JNIEnv* env);
+
+ int32_t waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets, InputWindow*& outFocusedWindow);
+ int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets, InputWindow*& outTouchedWindow);
+
+ void releaseTouchedWindowLd();
+
+ int32_t waitForNonTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+ int32_t waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+
+ bool interceptKeyBeforeDispatching(const InputTarget& target,
+ const KeyEvent* keyEvent, uint32_t policyFlags);
+
+ void pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType);
+ void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
+ bool checkInjectionPermission(const InputWindow* window,
+ int32_t injectorPid, int32_t injectorUid);
+
+ static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow);
+ static void addTarget(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
+
+ void registerMonitoringChannel(const sp<InputChannel>& inputChannel);
+ void unregisterMonitoringChannel(const sp<InputChannel>& inputChannel);
+ void addMonitoringTargetsLd(Vector<InputTarget>& outTargets);
+
+ static inline JNIEnv* jniEnv() {
+ return AndroidRuntime::getJNIEnv();
+ }
+
+ static bool isAppSwitchKey(int32_t keyCode);
+ static bool isPolicyKey(int32_t keyCode, bool isScreenOn);
+ static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
+};
+
+// ----------------------------------------------------------------------------
+
+NativeInputManager::NativeInputManager(jobject callbacksObj) :
+ mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+ mMaxEventsPerSecond(-1),
+ mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
+ mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
+ mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+ mFocusedApplication(NULL) {
+ JNIEnv* env = jniEnv();
+
+ mCallbacksObj = env->NewGlobalRef(callbacksObj);
+
+ sp<EventHub> eventHub = new EventHub();
+ mInputManager = new InputManager(eventHub, this, this);
+}
+
+NativeInputManager::~NativeInputManager() {
+ JNIEnv* env = jniEnv();
+
+ env->DeleteGlobalRef(mCallbacksObj);
+
+ releaseFocusedApplicationLd(env);
+}
+
+String8 NativeInputManager::dump() {
+ String8 dump;
+ { // acquire lock
+ AutoMutex _l(mDisplayLock);
+ dump.append("Native Input Dispatcher State:\n");
+ dumpDispatchStateLd(dump);
+ dump.append("\n");
+ } // release lock
+
+ dump.append("Input Devices:\n");
+ dumpDeviceInfo(dump);
+
+ return dump;
+}
+
+bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
+ return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+}
+
+bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) {
+ // Special keys that the WindowManagerPolicy might care about.
+ switch (keyCode) {
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_ENDCALL:
+ case AKEYCODE_POWER:
+ case AKEYCODE_CALL:
+ case AKEYCODE_HOME:
+ case AKEYCODE_MENU:
+ case AKEYCODE_SEARCH:
+ // media keys
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ return true;
+ default:
+ // We need to pass all keys to the policy in the following cases:
+ // - screen is off
+ // - keyguard is visible
+ // - policy is performing key chording
+ //return ! isScreenOn || keyguardVisible || chording;
+ return true; // XXX stubbed out for now
+ }
+}
+
+bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ return true;
+ }
+ return false;
+}
+
+void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
+ if (displayId == 0) {
+ AutoMutex _l(mDisplayLock);
+
+ mDisplayWidth = width;
+ mDisplayHeight = height;
+ }
+}
+
+void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orientation) {
+ if (displayId == 0) {
+ AutoMutex _l(mDisplayLock);
+
+ mDisplayOrientation = orientation;
+ }
+}
+
+status_t NativeInputManager::registerInputChannel(JNIEnv* env,
+ const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {
+ jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj);
+ if (! inputChannelObjWeak) {
+ LOGE("Could not create weak reference for input channel.");
+ LOGE_EX(env);
+ return NO_MEMORY;
+ }
+
+ status_t status;
+ {
+ AutoMutex _l(mInputChannelRegistryLock);
+
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
+ if (index >= 0) {
+ LOGE("Input channel object '%s' has already been registered",
+ inputChannel->getName().string());
+ status = INVALID_OPERATION;
+ goto DeleteWeakRef;
+ }
+
+ mInputChannelObjWeakTable.add(inputChannel.get(), inputChannelObjWeak);
+ }
+
+ status = mInputManager->registerInputChannel(inputChannel);
+ if (! status) {
+ // Success.
+ if (monitor) {
+ registerMonitoringChannel(inputChannel);
+ }
+ return OK;
+ }
+
+ // Failed!
+ {
+ AutoMutex _l(mInputChannelRegistryLock);
+ mInputChannelObjWeakTable.removeItem(inputChannel.get());
+ }
+
+DeleteWeakRef:
+ env->DeleteWeakGlobalRef(inputChannelObjWeak);
+ return status;
+}
+
+status_t NativeInputManager::unregisterInputChannel(JNIEnv* env,
+ const sp<InputChannel>& inputChannel) {
+ jweak inputChannelObjWeak;
+ {
+ AutoMutex _l(mInputChannelRegistryLock);
+
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
+ if (index < 0) {
+ LOGE("Input channel object '%s' is not currently registered",
+ inputChannel->getName().string());
+ return INVALID_OPERATION;
+ }
+
+ inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index);
+ mInputChannelObjWeakTable.removeItemsAt(index);
+ }
+
+ env->DeleteWeakGlobalRef(inputChannelObjWeak);
+
+ unregisterMonitoringChannel(inputChannel);
+
+ return mInputManager->unregisterInputChannel(inputChannel);
+}
+
+jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env,
+ const sp<InputChannel>& inputChannel) {
+ {
+ AutoMutex _l(mInputChannelRegistryLock);
+
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
+ if (index < 0) {
+ return NULL;
+ }
+
+ jweak inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index);
+ return env->NewLocalRef(inputChannelObjWeak);
+ }
+}
+
+bool NativeInputManager::getDisplayInfo(int32_t displayId,
+ int32_t* width, int32_t* height, int32_t* orientation) {
+ bool result = false;
+ if (displayId == 0) {
+ AutoMutex _l(mDisplayLock);
+
+ if (mDisplayWidth > 0) {
+ if (width) {
+ *width = mDisplayWidth;
+ }
+ if (height) {
+ *height = mDisplayHeight;
+ }
+ if (orientation) {
+ *orientation = mDisplayOrientation;
+ }
+ result = true;
+ }
+ }
+ return result;
+}
+
+bool NativeInputManager::isScreenOn() {
+ return android_server_PowerManagerService_isScreenOn();
+}
+
+bool NativeInputManager::isScreenBright() {
+ return android_server_PowerManagerService_isScreenBright();
+}
+
+void NativeInputManager::virtualKeyDownFeedback() {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("virtualKeyDownFeedback");
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyDownFeedback);
+ checkAndClearExceptionFromCallback(env, "virtualKeyDownFeedback");
+}
+
+int32_t NativeInputManager::interceptKey(nsecs_t when,
+ int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("interceptKey - when=%lld, deviceId=%d, down=%d, keyCode=%d, scanCode=%d, "
+ "policyFlags=0x%x",
+ when, deviceId, down, keyCode, scanCode, policyFlags);
+#endif
+
+ const int32_t WM_ACTION_PASS_TO_USER = 1;
+ const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
+ const int32_t WM_ACTION_GO_TO_SLEEP = 4;
+
+ bool isScreenOn = this->isScreenOn();
+ bool isScreenBright = this->isScreenBright();
+
+ jint wmActions = 0;
+ if (isPolicyKey(keyCode, isScreenOn)) {
+ JNIEnv* env = jniEnv();
+
+ wmActions = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeQueueing,
+ when, keyCode, down, policyFlags, isScreenOn);
+ if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
+ wmActions = 0;
+ }
+ } else {
+ wmActions = WM_ACTION_PASS_TO_USER;
+ }
+
+ int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
+ if (! isScreenOn) {
+ // Key presses and releases wake the device.
+ policyFlags |= POLICY_FLAG_WOKE_HERE;
+ }
+
+ if (! isScreenBright) {
+ // Key presses and releases brighten the screen if dimmed.
+ policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+ }
+
+ if (wmActions & WM_ACTION_GO_TO_SLEEP) {
+ android_server_PowerManagerService_goToSleep(when);
+ }
+
+ if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
+ pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT);
+ }
+
+ if (wmActions & WM_ACTION_PASS_TO_USER) {
+ actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
+
+ if (down && isAppSwitchKey(keyCode)) {
+ JNIEnv* env = jniEnv();
+
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
+ checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing");
+
+ actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
+ }
+ }
+
+ return actions;
+}
+
+int32_t NativeInputManager::interceptGeneric(nsecs_t when, uint32_t& policyFlags) {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("interceptGeneric - when=%lld, policyFlags=0x%x", when, policyFlags);
+#endif
+
+ int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
+ if (isScreenOn()) {
+ // Only dispatch events when the device is awake.
+ // Do not wake the device.
+ actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
+
+ if (! isScreenBright()) {
+ // Brighten the screen if dimmed.
+ policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+ }
+ }
+
+ return actions;
+}
+
+int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode,
+ int32_t switchValue, uint32_t& policyFlags) {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x",
+ when, switchCode, switchValue, policyFlags);
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ switch (switchCode) {
+ case SW_LID:
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
+ when, switchValue == 0);
+ checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged");
+ break;
+ }
+
+ return InputReaderPolicyInterface::ACTION_NONE;
+}
+
+bool NativeInputManager::filterTouchEvents() {
+ if (mFilterTouchEvents < 0) {
+ JNIEnv* env = jniEnv();
+
+ jboolean result = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.filterTouchEvents);
+ if (checkAndClearExceptionFromCallback(env, "filterTouchEvents")) {
+ result = false;
+ }
+
+ mFilterTouchEvents = result ? 1 : 0;
+ }
+ return mFilterTouchEvents;
+}
+
+bool NativeInputManager::filterJumpyTouchEvents() {
+ if (mFilterJumpyTouchEvents < 0) {
+ JNIEnv* env = jniEnv();
+
+ jboolean result = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.filterJumpyTouchEvents);
+ if (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) {
+ result = false;
+ }
+
+ mFilterJumpyTouchEvents = result ? 1 : 0;
+ }
+ return mFilterJumpyTouchEvents;
+}
+
+void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName,
+ Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
+ outVirtualKeyDefinitions.clear();
+
+ JNIEnv* env = jniEnv();
+
+ jstring deviceNameStr = env->NewStringUTF(deviceName.string());
+ if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
+ jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+ gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
+ if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
+ jsize length = env->GetArrayLength(result);
+ for (jsize i = 0; i < length; i++) {
+ jobject item = env->GetObjectArrayElement(result, i);
+
+ outVirtualKeyDefinitions.add();
+ outVirtualKeyDefinitions.editTop().scanCode =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
+ outVirtualKeyDefinitions.editTop().centerX =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
+ outVirtualKeyDefinitions.editTop().centerY =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
+ outVirtualKeyDefinitions.editTop().width =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
+ outVirtualKeyDefinitions.editTop().height =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
+
+ env->DeleteLocalRef(item);
+ }
+ env->DeleteLocalRef(result);
+ }
+ env->DeleteLocalRef(deviceNameStr);
+ }
+}
+
+void NativeInputManager::getInputDeviceCalibration(const String8& deviceName,
+ InputDeviceCalibration& outCalibration) {
+ outCalibration.clear();
+
+ JNIEnv* env = jniEnv();
+
+ jstring deviceNameStr = env->NewStringUTF(deviceName.string());
+ if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration")) {
+ jobject result = env->CallObjectMethod(mCallbacksObj,
+ gCallbacksClassInfo.getInputDeviceCalibration, deviceNameStr);
+ if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration") && result) {
+ jobjectArray keys = jobjectArray(env->GetObjectField(result,
+ gInputDeviceCalibrationClassInfo.keys));
+ jobjectArray values = jobjectArray(env->GetObjectField(result,
+ gInputDeviceCalibrationClassInfo.values));
+
+ jsize length = env->GetArrayLength(keys);
+ for (jsize i = 0; i < length; i++) {
+ jstring keyStr = jstring(env->GetObjectArrayElement(keys, i));
+ jstring valueStr = jstring(env->GetObjectArrayElement(values, i));
+
+ const char* keyChars = env->GetStringUTFChars(keyStr, NULL);
+ String8 key(keyChars);
+ env->ReleaseStringUTFChars(keyStr, keyChars);
+
+ const char* valueChars = env->GetStringUTFChars(valueStr, NULL);
+ String8 value(valueChars);
+ env->ReleaseStringUTFChars(valueStr, valueChars);
+
+ outCalibration.addProperty(key, value);
+
+ env->DeleteLocalRef(keyStr);
+ env->DeleteLocalRef(valueStr);
+ }
+ env->DeleteLocalRef(keys);
+ env->DeleteLocalRef(values);
+ env->DeleteLocalRef(result);
+ }
+ env->DeleteLocalRef(deviceNameStr);
+ }
+}
+
+void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
+ outExcludedDeviceNames.clear();
+
+ JNIEnv* env = jniEnv();
+
+ jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+ gCallbacksClassInfo.getExcludedDeviceNames));
+ if (! checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
+ jsize length = env->GetArrayLength(result);
+ for (jsize i = 0; i < length; i++) {
+ jstring item = jstring(env->GetObjectArrayElement(result, i));
+
+ const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
+ outExcludedDeviceNames.add(String8(deviceNameChars));
+ env->ReleaseStringUTFChars(item, deviceNameChars);
+
+ env->DeleteLocalRef(item);
+ }
+ env->DeleteLocalRef(result);
+ }
+}
+
+void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyConfigurationChanged - when=%lld", when);
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ InputConfiguration config;
+ mInputManager->getInputConfiguration(& config);
+
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
+ when, config.touchScreen, config.keyboard, config.navigation);
+ checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
+}
+
+void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+ if (inputChannelObjLocal) {
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelBroken,
+ inputChannelObjLocal);
+ checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");
+
+ env->DeleteLocalRef(inputChannelObjLocal);
+ }
+
+ unregisterMonitoringChannel(inputChannel);
+}
+
+bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+ nsecs_t& outNewTimeout) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyInputChannelANR - inputChannel='%s'",
+ inputChannel->getName().string());
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ jlong newTimeout;
+ jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+ if (inputChannelObjLocal) {
+ newTimeout = env->CallLongMethod(mCallbacksObj,
+ gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal);
+ if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) {
+ newTimeout = -2;
+ }
+
+ env->DeleteLocalRef(inputChannelObjLocal);
+ } else {
+ newTimeout = -2;
+ }
+
+ if (newTimeout == -2) {
+ return false; // abort
+ }
+
+ outNewTimeout = newTimeout;
+ return true; // resume
+}
+
+void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'",
+ inputChannel->getName().string());
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+ if (inputChannelObjLocal) {
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR,
+ inputChannelObjLocal);
+ checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR");
+
+ env->DeleteLocalRef(inputChannelObjLocal);
+ }
+}
+
+bool NativeInputManager::notifyANR(jobject tokenObj, nsecs_t& outNewTimeout) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyANR");
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ jlong newTimeout = env->CallLongMethod(mCallbacksObj,
+ gCallbacksClassInfo.notifyANR, tokenObj);
+ if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
+ newTimeout = -2;
+ }
+
+ if (newTimeout == -2) {
+ return false; // abort
+ }
+
+ outNewTimeout = newTimeout;
+ return true; // resume
+}
+
+nsecs_t NativeInputManager::getKeyRepeatTimeout() {
+ if (! isScreenOn()) {
+ // Disable key repeat when the screen is off.
+ return -1;
+ } else {
+ // TODO use ViewConfiguration.getLongPressTimeout()
+ return milliseconds_to_nanoseconds(500);
+ }
+}
+
+int32_t NativeInputManager::getMaxEventsPerSecond() {
+ if (mMaxEventsPerSecond < 0) {
+ JNIEnv* env = jniEnv();
+
+ jint result = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.getMaxEventsPerSecond);
+ if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) {
+ result = 60;
+ }
+
+ mMaxEventsPerSecond = result;
+ }
+ return mMaxEventsPerSecond;
+}
+
+void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
+#if DEBUG_FOCUS
+ LOGD("setInputWindows");
+#endif
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ sp<InputChannel> touchedWindowChannel;
+ if (mTouchedWindow) {
+ touchedWindowChannel = mTouchedWindow->inputChannel;
+ mTouchedWindow = NULL;
+ }
+ size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
+ if (numTouchedWallpapers != 0) {
+ for (size_t i = 0; i < numTouchedWallpapers; i++) {
+ mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
+ }
+ mTouchedWallpaperWindows.clear();
+ }
+
+ bool hadFocusedWindow = mFocusedWindow != NULL;
+
+ mWindows.clear();
+ mFocusedWindow = NULL;
+ mWallpaperWindows.clear();
+
+ if (windowObjArray) {
+ mWindowsReady = true;
+
+ jsize length = env->GetArrayLength(windowObjArray);
+ for (jsize i = 0; i < length; i++) {
+ jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
+ if (! inputTargetObj) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ mWindows.push();
+ InputWindow& window = mWindows.editTop();
+ bool valid = populateWindow(env, inputTargetObj, window);
+ if (! valid) {
+ mWindows.pop();
+ }
+
+ env->DeleteLocalRef(inputTargetObj);
+ }
+
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(i);
+ if (window->hasFocus) {
+ mFocusedWindow = window;
+ }
+
+ if (window->layoutParamsType == TYPE_WALLPAPER) {
+ mWallpaperWindows.push(window);
+
+ for (size_t j = 0; j < numTouchedWallpapers; j++) {
+ if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
+ mTouchedWallpaperWindows.push(window);
+ }
+ }
+ }
+
+ if (window->inputChannel == touchedWindowChannel) {
+ mTouchedWindow = window;
+ }
+ }
+ } else {
+ mWindowsReady = false;
+ }
+
+ mTempTouchedWallpaperChannels.clear();
+
+ if ((hadFocusedWindow && ! mFocusedWindow)
+ || (mFocusedWindow && ! mFocusedWindow->visible)) {
+ preemptInputDispatch();
+ }
+
+ mDispatchStateChanged.broadcast();
+
+#if DEBUG_FOCUS
+ logDispatchStateLd();
+#endif
+ } // release lock
+}
+
+bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
+ InputWindow& outWindow) {
+ bool valid = false;
+
+ jobject inputChannelObj = env->GetObjectField(windowObj,
+ gInputWindowClassInfo.inputChannel);
+ if (inputChannelObj) {
+ sp<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
+ if (inputChannel != NULL) {
+ jint layoutParamsFlags = env->GetIntField(windowObj,
+ gInputWindowClassInfo.layoutParamsFlags);
+ jint layoutParamsType = env->GetIntField(windowObj,
+ gInputWindowClassInfo.layoutParamsType);
+ jlong dispatchingTimeoutNanos = env->GetLongField(windowObj,
+ gInputWindowClassInfo.dispatchingTimeoutNanos);
+ jint frameLeft = env->GetIntField(windowObj,
+ gInputWindowClassInfo.frameLeft);
+ jint frameTop = env->GetIntField(windowObj,
+ gInputWindowClassInfo.frameTop);
+ jint touchableAreaLeft = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaLeft);
+ jint touchableAreaTop = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaTop);
+ jint touchableAreaRight = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaRight);
+ jint touchableAreaBottom = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaBottom);
+ jboolean visible = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.visible);
+ jboolean hasFocus = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.hasFocus);
+ jboolean hasWallpaper = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.hasWallpaper);
+ jboolean paused = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.paused);
+ jint ownerPid = env->GetIntField(windowObj,
+ gInputWindowClassInfo.ownerPid);
+ jint ownerUid = env->GetIntField(windowObj,
+ gInputWindowClassInfo.ownerUid);
+
+ outWindow.inputChannel = inputChannel;
+ outWindow.layoutParamsFlags = layoutParamsFlags;
+ outWindow.layoutParamsType = layoutParamsType;
+ outWindow.dispatchingTimeout = dispatchingTimeoutNanos;
+ outWindow.frameLeft = frameLeft;
+ outWindow.frameTop = frameTop;
+ outWindow.touchableAreaLeft = touchableAreaLeft;
+ outWindow.touchableAreaTop = touchableAreaTop;
+ outWindow.touchableAreaRight = touchableAreaRight;
+ outWindow.touchableAreaBottom = touchableAreaBottom;
+ outWindow.visible = visible;
+ outWindow.hasFocus = hasFocus;
+ outWindow.hasWallpaper = hasWallpaper;
+ outWindow.paused = paused;
+ outWindow.ownerPid = ownerPid;
+ outWindow.ownerUid = ownerUid;
+ valid = true;
+ } else {
+ LOGW("Dropping input target because its input channel is not initialized.");
+ }
+
+ env->DeleteLocalRef(inputChannelObj);
+ } else {
+ LOGW("Dropping input target because the input channel object was null.");
+ }
+ return valid;
+}
+
+void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) {
+#if DEBUG_FOCUS
+ LOGD("setFocusedApplication");
+#endif
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ releaseFocusedApplicationLd(env);
+
+ if (applicationObj) {
+ jstring nameObj = jstring(env->GetObjectField(applicationObj,
+ gInputApplicationClassInfo.name));
+ jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj,
+ gInputApplicationClassInfo.dispatchingTimeoutNanos);
+ jobject tokenObj = env->GetObjectField(applicationObj,
+ gInputApplicationClassInfo.token);
+ jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj);
+ if (! tokenObjWeak) {
+ LOGE("Could not create weak reference for application token.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+ env->DeleteLocalRef(tokenObj);
+
+ mFocusedApplication = & mFocusedApplicationStorage;
+
+ if (nameObj) {
+ const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
+ mFocusedApplication->name.setTo(nameStr);
+ env->ReleaseStringUTFChars(nameObj, nameStr);
+ env->DeleteLocalRef(nameObj);
+ } else {
+ LOGE("InputApplication.name should not be null.");
+ mFocusedApplication->name.setTo("unknown");
+ }
+
+ mFocusedApplication->dispatchingTimeout = dispatchingTimeoutNanos;
+ mFocusedApplication->tokenObjWeak = tokenObjWeak;
+ }
+
+ mDispatchStateChanged.broadcast();
+
+#if DEBUG_FOCUS
+ logDispatchStateLd();
+#endif
+ } // release lock
+}
+
+void NativeInputManager::releaseFocusedApplicationLd(JNIEnv* env) {
+ if (mFocusedApplication) {
+ env->DeleteWeakGlobalRef(mFocusedApplication->tokenObjWeak);
+ mFocusedApplication = NULL;
+ }
+}
+
+void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+ LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+ mDispatchEnabled = enabled;
+ mDispatchFrozen = frozen;
+
+ mDispatchStateChanged.broadcast();
+ }
+
+#if DEBUG_FOCUS
+ logDispatchStateLd();
+#endif
+ } // release lock
+}
+
+void NativeInputManager::preemptInputDispatch() {
+#if DEBUG_FOCUS
+ LOGD("preemptInputDispatch");
+#endif
+
+ mInputManager->preemptInputDispatch();
+}
+
+int32_t NativeInputManager::waitForFocusedWindowLd(uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+ InputWindow*& outFocusedWindow) {
+
+ int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ bool firstIteration = true;
+ ANRTimer anrTimer;
+ for (;;) {
+ if (firstIteration) {
+ firstIteration = false;
+ } else {
+ if (! anrTimer.waitForDispatchStateChangeLd(this)) {
+ LOGW("Dropping event because the dispatcher timed out waiting to identify "
+ "the window that should receive it.");
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+ }
+
+ // If dispatch is not enabled then fail.
+ if (! mDispatchEnabled) {
+ LOGI("Dropping event because input dispatch is disabled.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break;
+ }
+
+ // If dispatch is frozen or we don't have valid window data yet then wait.
+ if (mDispatchFrozen || ! mWindowsReady) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because dispatch is frozen or windows are not ready.");
+#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue;
+ }
+
+ // If there is no currently focused window and no focused application
+ // then drop the event.
+ if (! mFocusedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may yet introduce a new target: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ continue;
+ }
+
+ LOGI("Dropping event because there is no focused window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break;
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(mFocusedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ break;
+ }
+
+ // If the currently focused window is paused then keep waiting.
+ if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(mFocusedWindow);
+ continue;
+ }
+
+ // Success!
+ break; // done waiting, exit loop
+ }
+
+ // Output targets.
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ addTarget(mFocusedWindow, InputTarget::FLAG_SYNC,
+ anrTimer.getTimeSpentWaitingForApplication(), outTargets);
+
+ outFocusedWindow = mFocusedWindow;
+ } else {
+ outFocusedWindow = NULL;
+ }
+
+#if DEBUG_FOCUS
+ LOGD("waitForFocusedWindow finished: injectionResult=%d",
+ injectionResult);
+ logDispatchStateLd();
+#endif
+ return injectionResult;
+}
+
+enum InjectionPermission {
+ INJECTION_PERMISSION_UNKNOWN,
+ INJECTION_PERMISSION_GRANTED,
+ INJECTION_PERMISSION_DENIED
+};
+
+int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+ InputWindow*& outTouchedWindow) {
+ nsecs_t startTime = now();
+
+ // For security reasons, we defer updating the touch state until we are sure that
+ // event injection will be allowed.
+ //
+ // FIXME In the original code, screenWasOff could never be set to true.
+ // The reason is that the POLICY_FLAG_WOKE_HERE
+ // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+ // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
+ // actually enqueued using the policyFlags that appeared in the final EV_SYN
+ // events upon which no preprocessing took place. So policyFlags was always 0.
+ // In the new native input dispatcher we're a bit more careful about event
+ // preprocessing so the touches we receive can actually have non-zero policyFlags.
+ // Unfortunately we obtain undesirable behavior.
+ //
+ // Here's what happens:
+ //
+ // When the device dims in anticipation of going to sleep, touches
+ // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+ // the device to brighten and reset the user activity timer.
+ // Touches on other windows (such as the launcher window)
+ // are dropped. Then after a moment, the device goes to sleep. Oops.
+ //
+ // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+ // instead of POLICY_FLAG_WOKE_HERE...
+ //
+ bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+
+ int32_t action = motionEvent->getAction();
+
+ bool firstIteration = true;
+ ANRTimer anrTimer;
+ int32_t injectionResult;
+ InjectionPermission injectionPermission;
+ for (;;) {
+ if (firstIteration) {
+ firstIteration = false;
+ } else {
+ if (! anrTimer.waitForDispatchStateChangeLd(this)) {
+ LOGW("Dropping event because the dispatcher timed out waiting to identify "
+ "the window that should receive it.");
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ break; // timed out, exit wait loop
+ }
+ }
+
+ // If dispatch is not enabled then fail.
+ if (! mDispatchEnabled) {
+ LOGI("Dropping event because input dispatch is disabled.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ break; // failed, exit wait loop
+ }
+
+ // If dispatch is frozen or we don't have valid window data yet then wait.
+ if (mDispatchFrozen || ! mWindowsReady) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because dispatch is frozen or windows are not ready.");
+#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue;
+ }
+
+ // Update the touch state as needed based on the properties of the touch event.
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ /* Case 1: ACTION_DOWN */
+
+ InputWindow* newTouchedWindow = NULL;
+ mTempTouchedOutsideWindows.clear();
+
+ int32_t x = int32_t(motionEvent->getX(0));
+ int32_t y = int32_t(motionEvent->getY(0));
+ InputWindow* topErrorWindow = NULL;
+
+ // Traverse windows from front to back to find touched window and outside targets.
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(i);
+ int32_t flags = window->layoutParamsFlags;
+
+ if (flags & FLAG_SYSTEM_ERROR) {
+ if (! topErrorWindow) {
+ topErrorWindow = window;
+ }
+ }
+
+ if (window->visible) {
+ if (! (flags & FLAG_NOT_TOUCHABLE)) {
+ bool isTouchModal = (flags &
+ (FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL)) == 0;
+ if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+ if (! screenWasOff || flags & FLAG_TOUCHABLE_WHEN_WAKING) {
+ newTouchedWindow = window;
+ }
+ break; // found touched window, exit window loop
+ }
+ }
+
+ if (flags & FLAG_WATCH_OUTSIDE_TOUCH) {
+ mTempTouchedOutsideWindows.push(window);
+ }
+ }
+ }
+
+ // If there is an error window but it is not taking focus (typically because
+ // it is invisible) then wait for it. Any other focused window may in
+ // fact be in ANR state.
+ if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because system error window is pending.");
+#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue; // wait some more
+ }
+
+ // If we did not find a touched window then fail.
+ if (! newTouchedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may yet introduce a new target: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ continue;
+ }
+
+ LOGI("Dropping event because there is no touched window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ break; // failed, exit wait loop
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ break; // failed, exit wait loop
+ }
+
+ // If the touched window is paused then keep waiting.
+ if (newTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(newTouchedWindow);
+ continue; // wait some more
+ }
+
+ // Success! Update the touch dispatch state for real.
+ releaseTouchedWindowLd();
+
+ mTouchedWindow = newTouchedWindow;
+
+ if (newTouchedWindow->hasWallpaper) {
+ mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
+ }
+
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ break; // done
+ } else {
+ /* Case 2: Everything but ACTION_DOWN */
+
+ // Check permissions.
+ if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ break; // failed, exit wait loop
+ }
+
+ // If the pointer is not currently down, then ignore the event.
+ if (! mTouchDown) {
+ LOGI("Dropping event because the pointer is not down.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ break; // failed, exit wait loop
+ }
+
+ // If there is no currently touched window then fail.
+ if (! mTouchedWindow) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Dropping event because there is no touched window to receive it.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ break; // failed, exit wait loop
+ }
+
+ // If the touched window is paused then keep waiting.
+ if (mTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(mTouchedWindow);
+ continue; // wait some more
+ }
+
+ // Success!
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ break; // done
+ }
+ }
+
+ // Output targets.
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
+ for (size_t i = 0; i < numWallpaperWindows; i++) {
+ addTarget(mTouchedWallpaperWindows[i], 0, 0, outTargets);
+ }
+
+ size_t numOutsideWindows = mTempTouchedOutsideWindows.size();
+ for (size_t i = 0; i < numOutsideWindows; i++) {
+ addTarget(mTempTouchedOutsideWindows[i], InputTarget::FLAG_OUTSIDE, 0, outTargets);
+ }
+
+ addTarget(mTouchedWindow, InputTarget::FLAG_SYNC,
+ anrTimer.getTimeSpentWaitingForApplication(), outTargets);
+ outTouchedWindow = mTouchedWindow;
+ } else {
+ outTouchedWindow = NULL;
+ }
+ mTempTouchedOutsideWindows.clear();
+
+ // Check injection permission once and for all.
+ if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
+ if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
+ injectorPid, injectorUid)) {
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ } else {
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ }
+ }
+
+ // Update final pieces of touch state if the injector had permission.
+ if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ if (mTouchDown) {
+ // This is weird. We got a down but we thought it was already down!
+ LOGW("Pointer down received while already down.");
+ } else {
+ mTouchDown = true;
+ }
+
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ // Since we failed to identify a target for this touch down, we may still
+ // be holding on to an earlier target from a previous touch down. Release it.
+ releaseTouchedWindowLd();
+ }
+ } else if (action == AMOTION_EVENT_ACTION_UP) {
+ mTouchDown = false;
+ releaseTouchedWindowLd();
+ }
+ } else {
+ LOGW("Not updating touch focus because injection was denied.");
+ }
+
+#if DEBUG_FOCUS
+ LOGD("waitForTouchedWindow finished: injectionResult=%d",
+ injectionResult);
+ logDispatchStateLd();
+#endif
+ return injectionResult;
+}
+
+void NativeInputManager::releaseTouchedWindowLd() {
+ mTouchedWindow = NULL;
+ mTouchedWallpaperWindows.clear();
+}
+
+void NativeInputManager::addTarget(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets) {
+ nsecs_t timeout = window->dispatchingTimeout - timeSpentWaitingForApplication;
+ if (timeout < MIN_INPUT_DISPATCHING_TIMEOUT) {
+ timeout = MIN_INPUT_DISPATCHING_TIMEOUT;
+ }
+
+ outTargets.push();
+
+ InputTarget& target = outTargets.editTop();
+ target.inputChannel = window->inputChannel;
+ target.flags = targetFlags;
+ target.timeout = timeout;
+ target.xOffset = - window->frameLeft;
+ target.yOffset = - window->frameTop;
+}
+
+bool NativeInputManager::checkInjectionPermission(const InputWindow* window,
+ int32_t injectorPid, int32_t injectorUid) {
+ if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
+ JNIEnv* env = jniEnv();
+ jboolean result = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
+ checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
+
+ if (! result) {
+ if (window) {
+ LOGW("Permission denied: injecting event from pid %d uid %d to window "
+ "with input channel %s owned by uid %d",
+ injectorPid, injectorUid, window->inputChannel->getName().string(),
+ window->ownerUid);
+ } else {
+ LOGW("Permission denied: injecting event from pid %d uid %d",
+ injectorPid, injectorUid);
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("waitForKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ InputWindow* focusedWindow;
+ int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = focusedWindow->layoutParamsType;
+ } // release lock
+
+ if (isPolicyKey(keyEvent->getKeyCode(), isScreenOn())) {
+ const InputTarget& target = outTargets.top();
+ bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags);
+ if (consumed) {
+ outTargets.clear();
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+ }
+
+ addMonitoringTargetsLd(outTargets);
+ }
+
+ pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t source = motionEvent->getSource();
+ if (source & AINPUT_SOURCE_CLASS_POINTER) {
+ return waitForTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ outTargets);
+ } else {
+ return waitForNonTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ outTargets);
+ }
+}
+
+int32_t NativeInputManager::waitForNonTouchEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("waitForNonTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ InputWindow* focusedWindow;
+ int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = focusedWindow->layoutParamsType;
+
+ addMonitoringTargetsLd(outTargets);
+ } // release lock
+
+ pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+int32_t NativeInputManager::waitForTouchEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("waitForTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ InputWindow* touchedWindow;
+ int32_t injectionResult = waitForTouchedWindowLd(motionEvent, policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ touchedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = touchedWindow->layoutParamsType;
+
+ addMonitoringTargetsLd(outTargets);
+ } // release lock
+
+ int32_t eventType;
+ switch (motionEvent->getAction()) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ break;
+ case AMOTION_EVENT_ACTION_UP:
+ eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+ break;
+ default:
+ if (motionEvent->getEventTime() - motionEvent->getDownTime()
+ >= EVENT_IGNORE_DURATION) {
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ } else {
+ eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+ }
+ break;
+ }
+ pokeUserActivityIfNeeded(windowType, eventType);
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+bool NativeInputManager::interceptKeyBeforeDispatching(const InputTarget& target,
+ const KeyEvent* keyEvent, uint32_t policyFlags) {
+ JNIEnv* env = jniEnv();
+
+ jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel);
+ if (inputChannelObj) {
+ jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeDispatching,
+ inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
+ keyEvent->getKeyCode(), keyEvent->getMetaState(),
+ keyEvent->getRepeatCount(), policyFlags);
+ bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
+
+ env->DeleteLocalRef(inputChannelObj);
+
+ return consumed && ! error;
+ } else {
+ LOGW("Could not apply key dispatch policy because input channel '%s' is "
+ "no longer valid.", target.inputChannel->getName().string());
+ return false;
+ }
+}
+
+void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) {
+ if (windowType != TYPE_KEYGUARD) {
+ nsecs_t eventTime = now();
+ pokeUserActivity(eventTime, eventType);
+ }
+}
+
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+ android_server_PowerManagerService_userActivity(eventTime, eventType);
+}
+
+void NativeInputManager::registerMonitoringChannel(const sp<InputChannel>& inputChannel) {
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+ mMonitoringChannels.push(inputChannel);
+ } // release lock
+}
+
+void NativeInputManager::unregisterMonitoringChannel(const sp<InputChannel>& inputChannel) {
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ if (mMonitoringChannels[i] == inputChannel) {
+ mMonitoringChannels.removeAt(i);
+ break;
+ }
+ }
+ } // release lock
+}
+
+void NativeInputManager::addMonitoringTargetsLd(Vector<InputTarget>& outTargets) {
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ outTargets.push();
+
+ InputTarget& target = outTargets.editTop();
+ target.inputChannel = mMonitoringChannels[i];
+ target.flags = 0;
+ target.timeout = -1;
+ target.xOffset = 0;
+ target.yOffset = 0;
+ }
+}
+
+static void dumpMotionRange(String8& dump,
+ const char* name, const InputDeviceInfo::MotionRange* range) {
+ if (range) {
+ dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
+ name, range->min, range->max, range->flat, range->fuzz);
+ }
+}
+
+#define DUMP_MOTION_RANGE(range) \
+ dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
+
+void NativeInputManager::dumpDeviceInfo(String8& dump) {
+ Vector<int32_t> deviceIds;
+ mInputManager->getInputDeviceIds(deviceIds);
+
+ InputDeviceInfo deviceInfo;
+ for (size_t i = 0; i < deviceIds.size(); i++) {
+ int32_t deviceId = deviceIds[i];
+
+ status_t result = mInputManager->getInputDeviceInfo(deviceId, & deviceInfo);
+ if (result == NAME_NOT_FOUND) {
+ continue;
+ } else if (result != OK) {
+ dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n",
+ result);
+ continue;
+ }
+
+ dump.appendFormat(" Device %d: '%s'\n",
+ deviceInfo.getId(), deviceInfo.getName().string());
+ dump.appendFormat(" sources = 0x%08x\n",
+ deviceInfo.getSources());
+ dump.appendFormat(" keyboardType = %d\n",
+ deviceInfo.getKeyboardType());
+
+ dump.append(" motion ranges:\n");
+ DUMP_MOTION_RANGE(X);
+ DUMP_MOTION_RANGE(Y);
+ DUMP_MOTION_RANGE(PRESSURE);
+ DUMP_MOTION_RANGE(SIZE);
+ DUMP_MOTION_RANGE(TOUCH_MAJOR);
+ DUMP_MOTION_RANGE(TOUCH_MINOR);
+ DUMP_MOTION_RANGE(TOOL_MAJOR);
+ DUMP_MOTION_RANGE(TOOL_MINOR);
+ DUMP_MOTION_RANGE(ORIENTATION);
+ }
+}
+
+#undef DUMP_MOTION_RANGE
+
+void NativeInputManager::logDispatchStateLd() {
+ String8 dump;
+ dumpDispatchStateLd(dump);
+ LOGD("%s", dump.string());
+}
+
+void NativeInputManager::dumpDispatchStateLd(String8& dump) {
+ dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled);
+ dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen);
+ dump.appendFormat(" windowsReady: %d\n", mWindowsReady);
+
+ if (mFocusedApplication) {
+ dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
+ mFocusedApplication->name.string(),
+ mFocusedApplication->dispatchingTimeout / 1000000.0);
+ } else {
+ dump.append(" focusedApplication: <null>\n");
+ }
+ dump.appendFormat(" focusedWindow: '%s'\n",
+ mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
+ dump.appendFormat(" touchedWindow: '%s', touchDown=%d\n",
+ mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+ mTouchDown);
+ for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
+ dump.appendFormat(" touchedWallpaperWindows[%d]: '%s'\n",
+ i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
+ }
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ dump.appendFormat(" windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, "
+ "visible=%d, flags=0x%08x, type=0x%08x, "
+ "frame=[%d,%d], touchableArea=[%d,%d][%d,%d], "
+ "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+ i, mWindows[i].inputChannel->getName().string(),
+ mWindows[i].paused, mWindows[i].hasFocus, mWindows[i].hasWallpaper,
+ mWindows[i].visible, mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+ mWindows[i].frameLeft, mWindows[i].frameTop,
+ mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
+ mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
+ mWindows[i].ownerPid, mWindows[i].ownerUid,
+ mWindows[i].dispatchingTimeout / 1000000.0);
+ }
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ dump.appendFormat(" monitoringChannel[%d]: '%s'\n",
+ i, mMonitoringChannels[i]->getName().string());
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+NativeInputManager::ANRTimer::ANRTimer() :
+ mBudget(APPLICATION), mStartTime(now()), mFrozen(false), mPausedWindow(NULL) {
+}
+
+void NativeInputManager::ANRTimer::dispatchFrozenBySystem() {
+ mFrozen = true;
+}
+
+void NativeInputManager::ANRTimer::dispatchPausedByApplication(InputWindow* pausedWindow) {
+ mPausedWindow = pausedWindow;
+}
+
+bool NativeInputManager::ANRTimer::waitForDispatchStateChangeLd(NativeInputManager* inputManager) {
+ nsecs_t currentTime = now();
+
+ Budget newBudget;
+ nsecs_t dispatchingTimeout;
+ sp<InputChannel> pausedChannel = NULL;
+ jobject tokenObj = NULL;
+ if (mFrozen) {
+ newBudget = SYSTEM;
+ dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+ mFrozen = false;
+ } else if (mPausedWindow) {
+ newBudget = APPLICATION;
+ dispatchingTimeout = mPausedWindow->dispatchingTimeout;
+ pausedChannel = mPausedWindow->inputChannel;
+ mPausedWindow = NULL;
+ } else if (inputManager->mFocusedApplication) {
+ newBudget = APPLICATION;
+ dispatchingTimeout = inputManager->mFocusedApplication->dispatchingTimeout;
+ tokenObj = jniEnv()->NewLocalRef(inputManager->mFocusedApplication->tokenObjWeak);
+ } else {
+ newBudget = APPLICATION;
+ dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+ }
+
+ if (mBudget != newBudget) {
+ mBudget = newBudget;
+ mStartTime = currentTime;
+ }
+
+ bool result = false;
+ nsecs_t timeoutRemaining = mStartTime + dispatchingTimeout - currentTime;
+ if (timeoutRemaining > 0
+ && inputManager->mDispatchStateChanged.waitRelative(inputManager->mDispatchLock,
+ timeoutRemaining) == OK) {
+ result = true;
+ } else {
+ if (pausedChannel != NULL || tokenObj != NULL) {
+ bool resumed;
+ nsecs_t newTimeout = 0;
+
+ inputManager->mDispatchLock.unlock(); // release lock
+ if (pausedChannel != NULL) {
+ resumed = inputManager->notifyInputChannelANR(pausedChannel, /*out*/ newTimeout);
+ } else {
+ resumed = inputManager->notifyANR(tokenObj, /*out*/ newTimeout);
+ }
+ inputManager->mDispatchLock.lock(); // re-acquire lock
+
+ if (resumed) {
+ mStartTime = now() - dispatchingTimeout + newTimeout;
+ result = true;
+ }
+ }
+ }
+
+ if (tokenObj) {
+ jniEnv()->DeleteLocalRef(tokenObj);
+ }
+
+ return result;
+}
+
+nsecs_t NativeInputManager::ANRTimer::getTimeSpentWaitingForApplication() const {
+ return mBudget == APPLICATION ? now() - mStartTime : 0;
+}
+
+// ----------------------------------------------------------------------------
+
+static sp<NativeInputManager> gNativeInputManager;
+
+static bool checkInputManagerUnitialized(JNIEnv* env) {
+ if (gNativeInputManager == NULL) {
+ LOGE("Input manager not initialized.");
+ jniThrowRuntimeException(env, "Input manager not initialized.");
+ return true;
+ }
+ return false;
+}
+
+static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
+ jobject callbacks) {
+ if (gNativeInputManager == NULL) {
+ gNativeInputManager = new NativeInputManager(callbacks);
+ } else {
+ LOGE("Input manager already initialized.");
+ jniThrowRuntimeException(env, "Input manager already initialized.");
+ }
+}
+
+static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ status_t result = gNativeInputManager->getInputManager()->start();
+ if (result) {
+ jniThrowRuntimeException(env, "Input manager could not be started.");
+ }
+}
+
+static void android_server_InputManager_nativeSetDisplaySize(JNIEnv* env, jclass clazz,
+ jint displayId, jint width, jint height) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ // XXX we could get this from the SurfaceFlinger directly instead of requiring it
+ // to be passed in like this, not sure which is better but leaving it like this
+ // keeps the window manager in direct control of when display transitions propagate down
+ // to the input dispatcher
+ gNativeInputManager->setDisplaySize(displayId, width, height);
+}
+
+static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, jclass clazz,
+ jint displayId, jint orientation) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setDisplayOrientation(displayId, orientation);
+}
+
+static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
+ jint deviceId, jint sourceMask, jint scanCode) {
+ if (checkInputManagerUnitialized(env)) {
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ return gNativeInputManager->getInputManager()->getScanCodeState(
+ deviceId, uint32_t(sourceMask), scanCode);
+}
+
+static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
+ jint deviceId, jint sourceMask, jint keyCode) {
+ if (checkInputManagerUnitialized(env)) {
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ return gNativeInputManager->getInputManager()->getKeyCodeState(
+ deviceId, uint32_t(sourceMask), keyCode);
+}
+
+static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz,
+ jint deviceId, jint sourceMask, jint sw) {
+ if (checkInputManagerUnitialized(env)) {
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ return gNativeInputManager->getInputManager()->getSwitchState(
+ deviceId, uint32_t(sourceMask), sw);
+}
+
+static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz,
+ jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) {
+ if (checkInputManagerUnitialized(env)) {
+ return JNI_FALSE;
+ }
+
+ int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
+ uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
+ jsize numCodes = env->GetArrayLength(keyCodes);
+ jboolean result;
+ if (numCodes == env->GetArrayLength(keyCodes)) {
+ result = gNativeInputManager->getInputManager()->hasKeys(
+ deviceId, uint32_t(sourceMask), numCodes, codes, flags);
+ } else {
+ result = JNI_FALSE;
+ }
+
+ env->ReleaseBooleanArrayElements(outFlags, flags, 0);
+ env->ReleaseIntArrayElements(keyCodes, codes, 0);
+ return result;
+}
+
+static void throwInputChannelNotInitialized(JNIEnv* env) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "inputChannel is not initialized");
+}
+
+static void android_server_InputManager_handleInputChannelDisposed(JNIEnv* env,
+ jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data) {
+ LOGW("Input channel object '%s' was disposed without first being unregistered with "
+ "the input manager!", inputChannel->getName().string());
+
+ if (gNativeInputManager != NULL) {
+ gNativeInputManager->unregisterInputChannel(env, inputChannel);
+ }
+}
+
+static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
+ jobject inputChannelObj, jboolean monitor) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
+ inputChannelObj);
+ if (inputChannel == NULL) {
+ throwInputChannelNotInitialized(env);
+ return;
+ }
+
+
+ status_t status = gNativeInputManager->registerInputChannel(
+ env, inputChannel, inputChannelObj, monitor);
+ if (status) {
+ jniThrowRuntimeException(env, "Failed to register input channel. "
+ "Check logs for details.");
+ return;
+ }
+
+ if (! monitor) {
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
+ android_server_InputManager_handleInputChannelDisposed, NULL);
+ }
+}
+
+static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
+ jobject inputChannelObj) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
+ inputChannelObj);
+ if (inputChannel == NULL) {
+ throwInputChannelNotInitialized(env);
+ return;
+ }
+
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
+
+ status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);
+ if (status) {
+ jniThrowRuntimeException(env, "Failed to unregister input channel. "
+ "Check logs for details.");
+ }
+}
+
+static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jclass clazz,
+ jobject inputEventObj, jint injectorPid, jint injectorUid,
+ jint syncMode, jint timeoutMillis) {
+ if (checkInputManagerUnitialized(env)) {
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
+ if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
+ KeyEvent keyEvent;
+ android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);
+
+ return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
+ injectorPid, injectorUid, syncMode, timeoutMillis);
+ } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
+ MotionEvent motionEvent;
+ android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent);
+
+ return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
+ injectorPid, injectorUid, syncMode, timeoutMillis);
+ } else {
+ jniThrowRuntimeException(env, "Invalid input event type.");
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+}
+
+static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz,
+ jobjectArray windowObjArray) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setInputWindows(env, windowObjArray);
+}
+
+static void android_server_InputManager_nativeSetFocusedApplication(JNIEnv* env, jclass clazz,
+ jobject applicationObj) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setFocusedApplication(env, applicationObj);
+}
+
+static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env,
+ jclass clazz, jboolean enabled, jboolean frozen) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setInputDispatchMode(enabled, frozen);
+}
+
+static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env,
+ jclass clazz) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->preemptInputDispatch();
+}
+
+static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env,
+ jclass clazz, jint deviceId) {
+ if (checkInputManagerUnitialized(env)) {
+ return NULL;
+ }
+
+ InputDeviceInfo deviceInfo;
+ status_t status = gNativeInputManager->getInputManager()->getInputDeviceInfo(
+ deviceId, & deviceInfo);
+ if (status) {
+ return NULL;
+ }
+
+ jobject deviceObj = env->NewObject(gInputDeviceClassInfo.clazz, gInputDeviceClassInfo.ctor);
+ if (! deviceObj) {
+ return NULL;
+ }
+
+ jstring deviceNameObj = env->NewStringUTF(deviceInfo.getName().string());
+ if (! deviceNameObj) {
+ return NULL;
+ }
+
+ env->SetIntField(deviceObj, gInputDeviceClassInfo.mId, deviceInfo.getId());
+ env->SetObjectField(deviceObj, gInputDeviceClassInfo.mName, deviceNameObj);
+ env->SetIntField(deviceObj, gInputDeviceClassInfo.mSources, deviceInfo.getSources());
+ env->SetIntField(deviceObj, gInputDeviceClassInfo.mKeyboardType, deviceInfo.getKeyboardType());
+
+ const KeyedVector<int, InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
+ for (size_t i = 0; i < ranges.size(); i++) {
+ int rangeType = ranges.keyAt(i);
+ const InputDeviceInfo::MotionRange& range = ranges.valueAt(i);
+ env->CallVoidMethod(deviceObj, gInputDeviceClassInfo.addMotionRange,
+ rangeType, range.min, range.max, range.flat, range.fuzz);
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ }
+
+ return deviceObj;
+}
+
+static jintArray android_server_InputManager_nativeGetInputDeviceIds(JNIEnv* env,
+ jclass clazz) {
+ if (checkInputManagerUnitialized(env)) {
+ return NULL;
+ }
+
+ Vector<int> deviceIds;
+ gNativeInputManager->getInputManager()->getInputDeviceIds(deviceIds);
+
+ jintArray deviceIdsObj = env->NewIntArray(deviceIds.size());
+ if (! deviceIdsObj) {
+ return NULL;
+ }
+
+ env->SetIntArrayRegion(deviceIdsObj, 0, deviceIds.size(), deviceIds.array());
+ return deviceIdsObj;
+}
+
+static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) {
+ if (checkInputManagerUnitialized(env)) {
+ return NULL;
+ }
+
+ String8 dump(gNativeInputManager->dump());
+ return env->NewStringUTF(dump.string());
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gInputManagerMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V",
+ (void*) android_server_InputManager_nativeInit },
+ { "nativeStart", "()V",
+ (void*) android_server_InputManager_nativeStart },
+ { "nativeSetDisplaySize", "(III)V",
+ (void*) android_server_InputManager_nativeSetDisplaySize },
+ { "nativeSetDisplayOrientation", "(II)V",
+ (void*) android_server_InputManager_nativeSetDisplayOrientation },
+ { "nativeGetScanCodeState", "(III)I",
+ (void*) android_server_InputManager_nativeGetScanCodeState },
+ { "nativeGetKeyCodeState", "(III)I",
+ (void*) android_server_InputManager_nativeGetKeyCodeState },
+ { "nativeGetSwitchState", "(III)I",
+ (void*) android_server_InputManager_nativeGetSwitchState },
+ { "nativeHasKeys", "(II[I[Z)Z",
+ (void*) android_server_InputManager_nativeHasKeys },
+ { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;Z)V",
+ (void*) android_server_InputManager_nativeRegisterInputChannel },
+ { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
+ (void*) android_server_InputManager_nativeUnregisterInputChannel },
+ { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I",
+ (void*) android_server_InputManager_nativeInjectInputEvent },
+ { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V",
+ (void*) android_server_InputManager_nativeSetInputWindows },
+ { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V",
+ (void*) android_server_InputManager_nativeSetFocusedApplication },
+ { "nativeSetInputDispatchMode", "(ZZ)V",
+ (void*) android_server_InputManager_nativeSetInputDispatchMode },
+ { "nativePreemptInputDispatch", "()V",
+ (void*) android_server_InputManager_nativePreemptInputDispatch },
+ { "nativeGetInputDevice", "(I)Landroid/view/InputDevice;",
+ (void*) android_server_InputManager_nativeGetInputDevice },
+ { "nativeGetInputDeviceIds", "()[I",
+ (void*) android_server_InputManager_nativeGetInputDeviceIds },
+ { "nativeDump", "()Ljava/lang/String;",
+ (void*) android_server_InputManager_nativeDump },
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_server_InputManager(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "com/android/server/InputManager",
+ gInputManagerMethods, NELEM(gInputManagerMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ // Callbacks
+
+ FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks");
+
+ GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz,
+ "notifyConfigurationChanged", "(JIII)V");
+
+ GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz,
+ "notifyLidSwitchChanged", "(JZ)V");
+
+ GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz,
+ "notifyInputChannelBroken", "(Landroid/view/InputChannel;)V");
+
+ GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz,
+ "notifyInputChannelANR", "(Landroid/view/InputChannel;)J");
+
+ GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz,
+ "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V");
+
+ GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
+ "notifyANR", "(Ljava/lang/Object;)J");
+
+ GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz,
+ "virtualKeyDownFeedback", "()V");
+
+ GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
+ "interceptKeyBeforeQueueing", "(JIZIZ)I");
+
+ GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
+ "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIII)Z");
+
+ GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
+ "checkInjectEventsPermission", "(II)Z");
+
+ GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
+ "notifyAppSwitchComing", "()V");
+
+ GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz,
+ "filterTouchEvents", "()Z");
+
+ GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz,
+ "filterJumpyTouchEvents", "()Z");
+
+ GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyDefinitions, gCallbacksClassInfo.clazz,
+ "getVirtualKeyDefinitions",
+ "(Ljava/lang/String;)[Lcom/android/server/InputManager$VirtualKeyDefinition;");
+
+ GET_METHOD_ID(gCallbacksClassInfo.getInputDeviceCalibration, gCallbacksClassInfo.clazz,
+ "getInputDeviceCalibration",
+ "(Ljava/lang/String;)Lcom/android/server/InputManager$InputDeviceCalibration;");
+
+ GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
+ "getExcludedDeviceNames", "()[Ljava/lang/String;");
+
+ GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
+ "getMaxEventsPerSecond", "()I");
+
+ // VirtualKeyDefinition
+
+ FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
+ "com/android/server/InputManager$VirtualKeyDefinition");
+
+ GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.scanCode, gVirtualKeyDefinitionClassInfo.clazz,
+ "scanCode", "I");
+
+ GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerX, gVirtualKeyDefinitionClassInfo.clazz,
+ "centerX", "I");
+
+ GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerY, gVirtualKeyDefinitionClassInfo.clazz,
+ "centerY", "I");
+
+ GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.width, gVirtualKeyDefinitionClassInfo.clazz,
+ "width", "I");
+
+ GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
+ "height", "I");
+
+ // InputDeviceCalibration
+
+ FIND_CLASS(gInputDeviceCalibrationClassInfo.clazz,
+ "com/android/server/InputManager$InputDeviceCalibration");
+
+ GET_FIELD_ID(gInputDeviceCalibrationClassInfo.keys, gInputDeviceCalibrationClassInfo.clazz,
+ "keys", "[Ljava/lang/String;");
+
+ GET_FIELD_ID(gInputDeviceCalibrationClassInfo.values, gInputDeviceCalibrationClassInfo.clazz,
+ "values", "[Ljava/lang/String;");
+
+ // InputWindow
+
+ FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
+
+ GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
+ "inputChannel", "Landroid/view/InputChannel;");
+
+ GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz,
+ "layoutParamsFlags", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, gInputWindowClassInfo.clazz,
+ "layoutParamsType", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, gInputWindowClassInfo.clazz,
+ "dispatchingTimeoutNanos", "J");
+
+ GET_FIELD_ID(gInputWindowClassInfo.frameLeft, gInputWindowClassInfo.clazz,
+ "frameLeft", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz,
+ "frameTop", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaLeft, gInputWindowClassInfo.clazz,
+ "touchableAreaLeft", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaTop, gInputWindowClassInfo.clazz,
+ "touchableAreaTop", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaRight, gInputWindowClassInfo.clazz,
+ "touchableAreaRight", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaBottom, gInputWindowClassInfo.clazz,
+ "touchableAreaBottom", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz,
+ "visible", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz,
+ "hasFocus", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, gInputWindowClassInfo.clazz,
+ "hasWallpaper", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz,
+ "paused", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz,
+ "ownerPid", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.ownerUid, gInputWindowClassInfo.clazz,
+ "ownerUid", "I");
+
+ // InputApplication
+
+ FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/InputApplication");
+
+ GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz,
+ "name", "Ljava/lang/String;");
+
+ GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos,
+ gInputApplicationClassInfo.clazz,
+ "dispatchingTimeoutNanos", "J");
+
+ GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz,
+ "token", "Ljava/lang/Object;");
+
+ // KeyEvent
+
+ FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
+
+ // MotionEvent
+
+ FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
+
+ // InputDevice
+
+ FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
+
+ GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz,
+ "<init>", "()V");
+
+ GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
+ "addMotionRange", "(IFFFF)V");
+
+ GET_FIELD_ID(gInputDeviceClassInfo.mId, gInputDeviceClassInfo.clazz,
+ "mId", "I");
+
+ GET_FIELD_ID(gInputDeviceClassInfo.mName, gInputDeviceClassInfo.clazz,
+ "mName", "Ljava/lang/String;");
+
+ GET_FIELD_ID(gInputDeviceClassInfo.mSources, gInputDeviceClassInfo.clazz,
+ "mSources", "I");
+
+ GET_FIELD_ID(gInputDeviceClassInfo.mKeyboardType, gInputDeviceClassInfo.clazz,
+ "mKeyboardType", "I");
+
+ GET_FIELD_ID(gInputDeviceClassInfo.mMotionRanges, gInputDeviceClassInfo.clazz,
+ "mMotionRanges", "[Landroid/view/InputDevice$MotionRange;");
+
+ return 0;
+}
+
+} /* namespace android */
diff --git a/services/jni/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp
deleted file mode 100644
index c92f8df..0000000
--- a/services/jni/com_android_server_KeyInputQueue.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Input"
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include <utils/misc.h>
-#include <utils/Log.h>
-
-#include <ui/EventHub.h>
-#include <utils/threads.h>
-
-#include <stdio.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static struct input_offsets_t
-{
- jfieldID mMinValue;
- jfieldID mMaxValue;
- jfieldID mFlat;
- jfieldID mFuzz;
-
- jfieldID mDeviceId;
- jfieldID mType;
- jfieldID mScancode;
- jfieldID mKeycode;
- jfieldID mFlags;
- jfieldID mValue;
- jfieldID mWhen;
-} gInputOffsets;
-
-// ----------------------------------------------------------------------------
-
-static Mutex gLock;
-static sp<EventHub> gHub;
-
-static jboolean
-android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
- jobject event)
-{
- gLock.lock();
- sp<EventHub> hub = gHub;
- if (hub == NULL) {
- hub = new EventHub;
- gHub = hub;
- }
- gLock.unlock();
-
- int32_t deviceId;
- int32_t type;
- int32_t scancode, keycode;
- uint32_t flags;
- int32_t value;
- nsecs_t when;
- bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
- &flags, &value, &when);
-
- env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
- env->SetIntField(event, gInputOffsets.mType, (jint)type);
- env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
- env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
- env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
- env->SetIntField(event, gInputOffsets.mValue, value);
- env->SetLongField(event, gInputOffsets.mWhen,
- (jlong)(nanoseconds_to_milliseconds(when)));
-
- return res;
-}
-
-static jint
-android_server_KeyInputQueue_getDeviceClasses(JNIEnv* env, jobject clazz,
- jint deviceId)
-{
- jint classes = 0;
- gLock.lock();
- if (gHub != NULL) classes = gHub->getDeviceClasses(deviceId);
- gLock.unlock();
- return classes;
-}
-
-static jstring
-android_server_KeyInputQueue_getDeviceName(JNIEnv* env, jobject clazz,
- jint deviceId)
-{
- String8 name;
- gLock.lock();
- if (gHub != NULL) name = gHub->getDeviceName(deviceId);
- gLock.unlock();
-
- if (name.size() > 0) {
- return env->NewStringUTF(name.string());
- }
- return NULL;
-}
-
-static void
-android_server_KeyInputQueue_addExcludedDevice(JNIEnv* env, jobject clazz,
- jstring deviceName)
-{
- gLock.lock();
- sp<EventHub> hub = gHub;
- if (hub == NULL) {
- hub = new EventHub;
- gHub = hub;
- }
- gLock.unlock();
-
- const char* nameStr = env->GetStringUTFChars(deviceName, NULL);
- gHub->addExcludedDevice(nameStr);
- env->ReleaseStringUTFChars(deviceName, nameStr);
-}
-
-static jboolean
-android_server_KeyInputQueue_getAbsoluteInfo(JNIEnv* env, jobject clazz,
- jint deviceId, jint axis,
- jobject info)
-{
- int32_t minValue, maxValue, flat, fuzz;
- int res = -1;
- gLock.lock();
- if (gHub != NULL) {
- res = gHub->getAbsoluteInfo(deviceId, axis,
- &minValue, &maxValue, &flat, &fuzz);
- }
- gLock.unlock();
-
- if (res < 0) return JNI_FALSE;
-
- env->SetIntField(info, gInputOffsets.mMinValue, (jint)minValue);
- env->SetIntField(info, gInputOffsets.mMaxValue, (jint)maxValue);
- env->SetIntField(info, gInputOffsets.mFlat, (jint)flat);
- env->SetIntField(info, gInputOffsets.mFuzz, (jint)fuzz);
- return JNI_TRUE;
-}
-
-static jint
-android_server_KeyInputQueue_getSwitchState(JNIEnv* env, jobject clazz,
- jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getSwitchState(sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getSwitchStateDevice(JNIEnv* env, jobject clazz,
- jint deviceId, jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getSwitchState(deviceId, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getScancodeState(JNIEnv* env, jobject clazz,
- jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getScancodeState(sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getScancodeStateDevice(JNIEnv* env, jobject clazz,
- jint deviceId, jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getScancodeState(deviceId, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getKeycodeState(JNIEnv* env, jobject clazz,
- jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getKeycodeState(sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz,
- jint deviceId, jint sw)
-{
- jint st = -1;
- gLock.lock();
- if (gHub != NULL) st = gHub->getKeycodeState(deviceId, sw);
- gLock.unlock();
-
- return st;
-}
-
-static jint
-android_server_KeyInputQueue_scancodeToKeycode(JNIEnv* env, jobject clazz,
- jint deviceId, jint scancode)
-{
- jint res = 0;
- gLock.lock();
- if (gHub != NULL) {
- int32_t keycode;
- uint32_t flags;
- gHub->scancodeToKeycode(deviceId, scancode, &keycode, &flags);
- res = keycode;
- }
- gLock.unlock();
-
- return res;
-}
-
-static jboolean
-android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz,
- jintArray keyCodes, jbooleanArray outFlags)
-{
- jboolean ret = JNI_FALSE;
-
- int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
- uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
- size_t numCodes = env->GetArrayLength(keyCodes);
- if (numCodes == env->GetArrayLength(outFlags)) {
- gLock.lock();
- if (gHub != NULL) ret = gHub->hasKeys(numCodes, codes, flags);
- gLock.unlock();
- }
-
- env->ReleaseBooleanArrayElements(outFlags, flags, 0);
- env->ReleaseIntArrayElements(keyCodes, codes, 0);
- return ret;
-}
-
-// ----------------------------------------------------------------------------
-
-/*
- * JNI registration.
- */
-static JNINativeMethod gInputMethods[] = {
- /* name, signature, funcPtr */
- { "readEvent", "(Landroid/view/RawInputEvent;)Z",
- (void*) android_server_KeyInputQueue_readEvent },
- { "getDeviceClasses", "(I)I",
- (void*) android_server_KeyInputQueue_getDeviceClasses },
- { "getDeviceName", "(I)Ljava/lang/String;",
- (void*) android_server_KeyInputQueue_getDeviceName },
- { "addExcludedDevice", "(Ljava/lang/String;)V",
- (void*) android_server_KeyInputQueue_addExcludedDevice },
- { "getAbsoluteInfo", "(IILcom/android/server/InputDevice$AbsoluteInfo;)Z",
- (void*) android_server_KeyInputQueue_getAbsoluteInfo },
- { "getSwitchState", "(I)I",
- (void*) android_server_KeyInputQueue_getSwitchState },
- { "getSwitchState", "(II)I",
- (void*) android_server_KeyInputQueue_getSwitchStateDevice },
- { "nativeGetScancodeState", "(I)I",
- (void*) android_server_KeyInputQueue_getScancodeState },
- { "nativeGetScancodeState", "(II)I",
- (void*) android_server_KeyInputQueue_getScancodeStateDevice },
- { "nativeGetKeycodeState", "(I)I",
- (void*) android_server_KeyInputQueue_getKeycodeState },
- { "nativeGetKeycodeState", "(II)I",
- (void*) android_server_KeyInputQueue_getKeycodeStateDevice },
- { "hasKeys", "([I[Z)Z",
- (void*) android_server_KeyInputQueue_hasKeys },
- { "scancodeToKeycode", "(II)I",
- (void*) android_server_KeyInputQueue_scancodeToKeycode },
-};
-
-int register_android_server_KeyInputQueue(JNIEnv* env)
-{
- jclass input = env->FindClass("com/android/server/KeyInputQueue");
- LOG_FATAL_IF(input == NULL, "Unable to find class com/android/server/KeyInputQueue");
- int res = jniRegisterNativeMethods(env, "com/android/server/KeyInputQueue",
- gInputMethods, NELEM(gInputMethods));
-
- jclass absoluteInfo = env->FindClass("com/android/server/InputDevice$AbsoluteInfo");
- LOG_FATAL_IF(absoluteInfo == NULL, "Unable to find class com/android/server/InputDevice$AbsoluteInfo");
-
- gInputOffsets.mMinValue
- = env->GetFieldID(absoluteInfo, "minValue", "I");
- LOG_FATAL_IF(gInputOffsets.mMinValue == NULL, "Unable to find InputDevice.AbsoluteInfo.minValue");
-
- gInputOffsets.mMaxValue
- = env->GetFieldID(absoluteInfo, "maxValue", "I");
- LOG_FATAL_IF(gInputOffsets.mMaxValue == NULL, "Unable to find InputDevice.AbsoluteInfo.maxValue");
-
- gInputOffsets.mFlat
- = env->GetFieldID(absoluteInfo, "flat", "I");
- LOG_FATAL_IF(gInputOffsets.mFlat == NULL, "Unable to find InputDevice.AbsoluteInfo.flat");
-
- gInputOffsets.mFuzz
- = env->GetFieldID(absoluteInfo, "fuzz", "I");
- LOG_FATAL_IF(gInputOffsets.mFuzz == NULL, "Unable to find InputDevice.AbsoluteInfo.fuzz");
-
- jclass inputEvent = env->FindClass("android/view/RawInputEvent");
- LOG_FATAL_IF(inputEvent == NULL, "Unable to find class android/view/RawInputEvent");
-
- gInputOffsets.mDeviceId
- = env->GetFieldID(inputEvent, "deviceId", "I");
- LOG_FATAL_IF(gInputOffsets.mDeviceId == NULL, "Unable to find RawInputEvent.deviceId");
-
- gInputOffsets.mType
- = env->GetFieldID(inputEvent, "type", "I");
- LOG_FATAL_IF(gInputOffsets.mType == NULL, "Unable to find RawInputEvent.type");
-
- gInputOffsets.mScancode
- = env->GetFieldID(inputEvent, "scancode", "I");
- LOG_FATAL_IF(gInputOffsets.mScancode == NULL, "Unable to find RawInputEvent.scancode");
-
- gInputOffsets.mKeycode
- = env->GetFieldID(inputEvent, "keycode", "I");
- LOG_FATAL_IF(gInputOffsets.mKeycode == NULL, "Unable to find RawInputEvent.keycode");
-
- gInputOffsets.mFlags
- = env->GetFieldID(inputEvent, "flags", "I");
- LOG_FATAL_IF(gInputOffsets.mFlags == NULL, "Unable to find RawInputEvent.flags");
-
- gInputOffsets.mValue
- = env->GetFieldID(inputEvent, "value", "I");
- LOG_FATAL_IF(gInputOffsets.mValue == NULL, "Unable to find RawInputEvent.value");
-
- gInputOffsets.mWhen
- = env->GetFieldID(inputEvent, "when", "J");
- LOG_FATAL_IF(gInputOffsets.mWhen == NULL, "Unable to find RawInputEvent.when");
-
- return res;
-}
-
-}; // namespace android
-
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
new file mode 100644
index 0000000..146c177
--- /dev/null
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerManagerService-JNI"
+
+//#define LOG_NDEBUG 0
+
+#include "JNIHelp.h"
+#include "jni.h"
+#include <limits.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Timers.h>
+#include "com_android_server_PowerManagerService.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+
+ jmethodID goToSleep;
+ jmethodID userActivity;
+} gPowerManagerServiceClassInfo;
+
+// ----------------------------------------------------------------------------
+
+static jobject gPowerManagerServiceObj;
+
+static Mutex gPowerManagerLock;
+static bool gScreenOn;
+static bool gScreenBright;
+
+static nsecs_t gLastEventTime[POWER_MANAGER_LAST_EVENT + 1];
+
+// Throttling interval for user activity calls.
+static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms
+
+// ----------------------------------------------------------------------------
+
+static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ return true;
+ }
+ return false;
+}
+
+bool android_server_PowerManagerService_isScreenOn() {
+ AutoMutex _l(gPowerManagerLock);
+ return gScreenOn;
+}
+
+bool android_server_PowerManagerService_isScreenBright() {
+ AutoMutex _l(gPowerManagerLock);
+ return gScreenBright;
+}
+
+void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
+ if (gPowerManagerServiceObj) {
+ // Throttle calls into user activity by event type.
+ // We're a little conservative about argument checking here in case the caller
+ // passes in bad data which could corrupt system state.
+ if (eventType >= 0 && eventType <= POWER_MANAGER_LAST_EVENT) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (eventTime > now) {
+ eventTime = now;
+ }
+
+ if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) {
+ return;
+ }
+ gLastEventTime[eventType] = eventTime;
+ }
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity,
+ nanoseconds_to_milliseconds(eventTime), false, eventType, false);
+ checkAndClearExceptionFromCallback(env, "userActivity");
+ }
+}
+
+void android_server_PowerManagerService_goToSleep(nsecs_t eventTime) {
+ if (gPowerManagerServiceObj) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.goToSleep,
+ nanoseconds_to_milliseconds(eventTime));
+ checkAndClearExceptionFromCallback(env, "goToSleep");
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void android_server_PowerManagerService_nativeInit(JNIEnv* env, jobject obj) {
+ gPowerManagerServiceObj = env->NewGlobalRef(obj);
+}
+
+static void android_server_PowerManagerService_nativeSetPowerState(JNIEnv* env,
+ jobject serviceObj, jboolean screenOn, jboolean screenBright) {
+ AutoMutex _l(gPowerManagerLock);
+ gScreenOn = screenOn;
+ gScreenBright = screenBright;
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gPowerManagerServiceMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeInit", "()V",
+ (void*) android_server_PowerManagerService_nativeInit },
+ { "nativeSetPowerState", "(ZZ)V",
+ (void*) android_server_PowerManagerService_nativeSetPowerState },
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_server_PowerManagerService(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "com/android/server/PowerManagerService",
+ gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ // Callbacks
+
+ FIND_CLASS(gPowerManagerServiceClassInfo.clazz, "com/android/server/PowerManagerService");
+
+ GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, gPowerManagerServiceClassInfo.clazz,
+ "goToSleep", "(J)V");
+
+ GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz,
+ "userActivity", "(JZIZ)V");
+
+ // Initialize
+ for (int i = 0; i < POWER_MANAGER_LAST_EVENT; i++) {
+ gLastEventTime[i] = LLONG_MIN;
+ }
+ gScreenOn = true;
+ gScreenBright = true;
+ return 0;
+}
+
+} /* namespace android */
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
new file mode 100644
index 0000000..7c329b2
--- /dev/null
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_POWER_MANAGER_SERVICE_H
+#define _ANDROID_SERVER_POWER_MANAGER_SERVICE_H
+
+#include "JNIHelp.h"
+#include "jni.h"
+
+namespace android {
+
+enum {
+ POWER_MANAGER_OTHER_EVENT = 0,
+ POWER_MANAGER_CHEEK_EVENT = 1,
+ POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
+ // up events or LONG_TOUCH events.
+ POWER_MANAGER_LONG_TOUCH_EVENT = 3,
+ POWER_MANAGER_TOUCH_UP_EVENT = 4,
+ POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+
+ POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
+};
+
+extern bool android_server_PowerManagerService_isScreenOn();
+extern bool android_server_PowerManagerService_isScreenBright();
+extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType);
+extern void android_server_PowerManagerService_goToSleep(nsecs_t eventTime);
+
+} // namespace android
+
+#endif // _ANDROID_SERVER_POWER_MANAGER_SERVICE_H
diff --git a/services/jni/com_android_server_SensorService.cpp b/services/jni/com_android_server_SensorService.cpp
deleted file mode 100644
index 77db6da..0000000
--- a/services/jni/com_android_server_SensorService.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SensorService"
-
-#include "utils/Log.h"
-
-#include <hardware/sensors.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-
-namespace android {
-
-static struct file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
-static struct parcel_file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
-} gParcelFileDescriptorOffsets;
-
-static struct bundle_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jmethodID mPutIntArray;
- jmethodID mPutParcelableArray;
-} gBundleOffsets;
-
-/*
- * The method below are not thread-safe and not intended to be
- */
-
-static sensors_control_device_t* sSensorDevice = 0;
-
-static jint
-android_init(JNIEnv *env, jclass clazz)
-{
- sensors_module_t* module;
- if (hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
- if (sensors_control_open(&module->common, &sSensorDevice) == 0) {
- const struct sensor_t* list;
- int count = module->get_sensors_list(module, &list);
- return count;
- }
- }
- return 0;
-}
-
-static jobject
-android_open(JNIEnv *env, jclass clazz)
-{
- native_handle_t* handle = sSensorDevice->open_data_source(sSensorDevice);
- if (!handle) {
- return NULL;
- }
-
- // new Bundle()
- jobject bundle = env->NewObject(
- gBundleOffsets.mClass,
- gBundleOffsets.mConstructor);
-
- if (handle->numFds > 0) {
- jobjectArray fdArray = env->NewObjectArray(handle->numFds,
- gParcelFileDescriptorOffsets.mClass, NULL);
- for (int i = 0; i < handle->numFds; i++) {
- // new FileDescriptor()
- jobject fd = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- env->SetIntField(fd, gFileDescriptorOffsets.mDescriptor, handle->data[i]);
- // new ParcelFileDescriptor()
- jobject pfd = env->NewObject(gParcelFileDescriptorOffsets.mClass,
- gParcelFileDescriptorOffsets.mConstructor, fd);
- env->SetObjectArrayElement(fdArray, i, pfd);
- }
- // bundle.putParcelableArray("fds", fdArray);
- env->CallVoidMethod(bundle, gBundleOffsets.mPutParcelableArray,
- env->NewStringUTF("fds"), fdArray);
- }
-
- if (handle->numInts > 0) {
- jintArray intArray = env->NewIntArray(handle->numInts);
- env->SetIntArrayRegion(intArray, 0, handle->numInts, &handle->data[handle->numInts]);
- // bundle.putIntArray("ints", intArray);
- env->CallVoidMethod(bundle, gBundleOffsets.mPutIntArray,
- env->NewStringUTF("ints"), intArray);
- }
-
- // delete the file handle, but don't close any file descriptors
- native_handle_delete(handle);
- return bundle;
-}
-
-static jint
-android_close(JNIEnv *env, jclass clazz)
-{
- if (sSensorDevice->close_data_source)
- return sSensorDevice->close_data_source(sSensorDevice);
- else
- return 0;
-}
-
-static jboolean
-android_activate(JNIEnv *env, jclass clazz, jint sensor, jboolean activate)
-{
- int active = sSensorDevice->activate(sSensorDevice, sensor, activate);
- return (active<0) ? false : true;
-}
-
-static jint
-android_set_delay(JNIEnv *env, jclass clazz, jint ms)
-{
- return sSensorDevice->set_delay(sSensorDevice, ms);
-}
-
-static jint
-android_data_wake(JNIEnv *env, jclass clazz)
-{
- int res = sSensorDevice->wake(sSensorDevice);
- return res;
-}
-
-
-static JNINativeMethod gMethods[] = {
- {"_sensors_control_init", "()I", (void*) android_init },
- {"_sensors_control_open", "()Landroid/os/Bundle;", (void*) android_open },
- {"_sensors_control_close", "()I", (void*) android_close },
- {"_sensors_control_activate", "(IZ)Z", (void*) android_activate },
- {"_sensors_control_wake", "()I", (void*) android_data_wake },
- {"_sensors_control_set_delay","(I)I", (void*) android_set_delay },
-};
-
-int register_android_server_SensorService(JNIEnv *env)
-{
- jclass clazz;
-
- clazz = env->FindClass("java/io/FileDescriptor");
- gFileDescriptorOffsets.mClass = (jclass)env->NewGlobalRef(clazz);
- gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
- gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
-
- clazz = env->FindClass("android/os/ParcelFileDescriptor");
- gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>",
- "(Ljava/io/FileDescriptor;)V");
-
- clazz = env->FindClass("android/os/Bundle");
- gBundleOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gBundleOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
- gBundleOffsets.mPutIntArray = env->GetMethodID(clazz, "putIntArray", "(Ljava/lang/String;[I)V");
- gBundleOffsets.mPutParcelableArray = env->GetMethodID(clazz, "putParcelableArray",
- "(Ljava/lang/String;[Landroid/os/Parcelable;)V");
-
- return jniRegisterNativeMethods(env, "com/android/server/SensorService",
- gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
new file mode 100755
index 0000000..59d7cde
--- /dev/null
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GpsLocationProvider"
+
+//#define LOG_NDEBUG 0
+
+#include "JNIHelp.h"
+#include "jni.h"
+#include "hardware/hardware.h"
+#include "hardware/gps.h"
+#include "hardware_legacy/power.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <string.h>
+#include <pthread.h>
+
+static jobject mCallbacksObj = NULL;
+
+static jmethodID method_reportLocation;
+static jmethodID method_reportStatus;
+static jmethodID method_reportSvStatus;
+static jmethodID method_reportAGpsStatus;
+static jmethodID method_reportNmea;
+static jmethodID method_setEngineCapabilities;
+static jmethodID method_xtraDownloadRequest;
+static jmethodID method_reportNiNotification;
+
+static const GpsInterface* sGpsInterface = NULL;
+static const GpsXtraInterface* sGpsXtraInterface = NULL;
+static const AGpsInterface* sAGpsInterface = NULL;
+static const GpsNiInterface* sGpsNiInterface = NULL;
+static const GpsDebugInterface* sGpsDebugInterface = NULL;
+
+// temporary storage for GPS callbacks
+static GpsSvStatus sGpsSvStatus;
+static const char* sNmeaString;
+static int sNmeaStringLength;
+
+#define WAKE_LOCK_NAME "GPS"
+
+namespace android {
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+static void location_callback(GpsLocation* location)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mCallbacksObj, method_reportLocation, location->flags,
+ (jdouble)location->latitude, (jdouble)location->longitude,
+ (jdouble)location->altitude,
+ (jfloat)location->speed, (jfloat)location->bearing,
+ (jfloat)location->accuracy, (jlong)location->timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void status_callback(GpsStatus* status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mCallbacksObj, method_reportStatus, status->status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void sv_status_callback(GpsSvStatus* sv_status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ memcpy(&sGpsSvStatus, sv_status, sizeof(sGpsSvStatus));
+ env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ // The Java code will call back to read these values
+ // We do this to avoid creating unnecessary String objects
+ sNmeaString = nmea;
+ sNmeaStringLength = length;
+ env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void set_capabilities_callback(uint32_t capabilities)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+static void acquire_wakelock_callback()
+{
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
+}
+
+static void release_wakelock_callback()
+{
+ release_wake_lock(WAKE_LOCK_NAME);
+}
+
+static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)
+{
+ return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
+}
+
+GpsCallbacks sGpsCallbacks = {
+ sizeof(GpsCallbacks),
+ location_callback,
+ status_callback,
+ sv_status_callback,
+ nmea_callback,
+ set_capabilities_callback,
+ acquire_wakelock_callback,
+ release_wakelock_callback,
+ create_thread_callback,
+};
+
+static void xtra_download_request_callback()
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+GpsXtraCallbacks sGpsXtraCallbacks = {
+ xtra_download_request_callback,
+ create_thread_callback,
+};
+
+static void agps_status_callback(AGpsStatus* agps_status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus,
+ agps_status->type, agps_status->status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+AGpsCallbacks sAGpsCallbacks = {
+ agps_status_callback,
+ create_thread_callback,
+};
+
+static void gps_ni_notify_callback(GpsNiNotification *notification)
+{
+ LOGD("gps_ni_notify_callback\n");
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jstring requestor_id = env->NewStringUTF(notification->requestor_id);
+ jstring text = env->NewStringUTF(notification->text);
+ jstring extras = env->NewStringUTF(notification->extras);
+
+ if (requestor_id && text && extras) {
+ env->CallVoidMethod(mCallbacksObj, method_reportNiNotification,
+ notification->notification_id, notification->ni_type,
+ notification->notify_flags, notification->timeout,
+ notification->default_response, requestor_id, text,
+ notification->requestor_id_encoding,
+ notification->text_encoding, extras);
+ } else {
+ LOGE("out of memory in gps_ni_notify_callback\n");
+ }
+
+ if (requestor_id)
+ env->DeleteLocalRef(requestor_id);
+ if (text)
+ env->DeleteLocalRef(text);
+ if (extras)
+ env->DeleteLocalRef(extras);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+GpsNiCallbacks sGpsNiCallbacks = {
+ gps_ni_notify_callback,
+ create_thread_callback,
+};
+
+static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
+ method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
+ method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
+ method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
+ method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V");
+ method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
+ method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
+ method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
+ method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
+}
+
+static const GpsInterface* get_gps_interface() {
+ int err;
+ hw_module_t* module;
+ const GpsInterface* interface = NULL;
+
+ err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
+ if (err == 0) {
+ hw_device_t* device;
+ err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
+ if (err == 0) {
+ gps_device_t* gps_device = (gps_device_t *)device;
+ interface = gps_device->get_gps_interface(gps_device);
+ }
+ }
+
+ return interface;
+}
+
+static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) {
+ if (!sGpsInterface)
+ sGpsInterface = get_gps_interface();
+ return (sGpsInterface != NULL);
+}
+
+static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
+{
+ // this must be set before calling into the HAL library
+ if (!mCallbacksObj)
+ mCallbacksObj = env->NewGlobalRef(obj);
+
+ if (!sGpsInterface)
+ sGpsInterface = get_gps_interface();
+ if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
+ return false;
+
+ if (!sAGpsInterface)
+ sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
+ if (sAGpsInterface)
+ sAGpsInterface->init(&sAGpsCallbacks);
+
+ if (!sGpsNiInterface)
+ sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+ if (sGpsNiInterface)
+ sGpsNiInterface->init(&sGpsNiCallbacks);
+
+ if (!sGpsDebugInterface)
+ sGpsDebugInterface = (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
+
+ return true;
+}
+
+static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj)
+{
+ sGpsInterface->cleanup();
+}
+
+static jboolean android_location_GpsLocationProvider_set_position_mode(JNIEnv* env, jobject obj,
+ jint mode, jint recurrence, jint min_interval, jint preferred_accuracy, jint preferred_time)
+{
+ return (sGpsInterface->set_position_mode(mode, recurrence, min_interval, preferred_accuracy,
+ preferred_time) == 0);
+}
+
+static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj)
+{
+ return (sGpsInterface->start() == 0);
+}
+
+static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj)
+{
+ return (sGpsInterface->stop() == 0);
+}
+
+static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags)
+{
+ sGpsInterface->delete_aiding_data(flags);
+}
+
+static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj,
+ jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray,
+ jintArray maskArray)
+{
+ // this should only be called from within a call to reportSvStatus
+
+ jint* prns = env->GetIntArrayElements(prnArray, 0);
+ jfloat* snrs = env->GetFloatArrayElements(snrArray, 0);
+ jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
+ jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
+ jint* mask = env->GetIntArrayElements(maskArray, 0);
+
+ int num_svs = sGpsSvStatus.num_svs;
+ for (int i = 0; i < num_svs; i++) {
+ prns[i] = sGpsSvStatus.sv_list[i].prn;
+ snrs[i] = sGpsSvStatus.sv_list[i].snr;
+ elev[i] = sGpsSvStatus.sv_list[i].elevation;
+ azim[i] = sGpsSvStatus.sv_list[i].azimuth;
+ }
+ mask[0] = sGpsSvStatus.ephemeris_mask;
+ mask[1] = sGpsSvStatus.almanac_mask;
+ mask[2] = sGpsSvStatus.used_in_fix_mask;
+
+ env->ReleaseIntArrayElements(prnArray, prns, 0);
+ env->ReleaseFloatArrayElements(snrArray, snrs, 0);
+ env->ReleaseFloatArrayElements(elevArray, elev, 0);
+ env->ReleaseFloatArrayElements(azumArray, azim, 0);
+ env->ReleaseIntArrayElements(maskArray, mask, 0);
+ return num_svs;
+}
+
+static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj,
+ jbyteArray nmeaArray, jint buffer_size)
+{
+ // this should only be called from within a call to reportNmea
+ jbyte* nmea = (jbyte *)env->GetPrimitiveArrayCritical(nmeaArray, 0);
+ int length = sNmeaStringLength;
+ if (length > buffer_size)
+ length = buffer_size;
+ memcpy(nmea, sNmeaString, length);
+ env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);
+ return length;
+}
+
+static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj,
+ jlong time, jlong timeReference, jint uncertainty)
+{
+ sGpsInterface->inject_time(time, timeReference, uncertainty);
+}
+
+static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj,
+ jdouble latitude, jdouble longitude, jfloat accuracy)
+{
+ sGpsInterface->inject_location(latitude, longitude, accuracy);
+}
+
+static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj)
+{
+ if (!sGpsXtraInterface) {
+ sGpsXtraInterface = (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
+ if (sGpsXtraInterface) {
+ int result = sGpsXtraInterface->init(&sGpsXtraCallbacks);
+ if (result) {
+ sGpsXtraInterface = NULL;
+ }
+ }
+ }
+
+ return (sGpsXtraInterface != NULL);
+}
+
+static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj,
+ jbyteArray data, jint length)
+{
+ jbyte* bytes = (jbyte *)env->GetPrimitiveArrayCritical(data, 0);
+ sGpsXtraInterface->inject_xtra_data((char *)bytes, length);
+ env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
+}
+
+static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn)
+{
+ if (!sAGpsInterface) {
+ sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
+ }
+ if (sAGpsInterface) {
+ if (apn == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+ const char *apnStr = env->GetStringUTFChars(apn, NULL);
+ sAGpsInterface->data_conn_open(apnStr);
+ env->ReleaseStringUTFChars(apn, apnStr);
+ }
+}
+
+static void android_location_GpsLocationProvider_agps_data_conn_closed(JNIEnv* env, jobject obj)
+{
+ if (!sAGpsInterface) {
+ sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
+ }
+ if (sAGpsInterface) {
+ sAGpsInterface->data_conn_closed();
+ }
+}
+
+static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* env, jobject obj)
+{
+ if (!sAGpsInterface) {
+ sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
+ }
+ if (sAGpsInterface) {
+ sAGpsInterface->data_conn_failed();
+ }
+}
+
+static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj,
+ jint type, jstring hostname, jint port)
+{
+ if (!sAGpsInterface) {
+ sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
+ }
+ if (sAGpsInterface) {
+ const char *c_hostname = env->GetStringUTFChars(hostname, NULL);
+ sAGpsInterface->set_server(type, c_hostname, port);
+ env->ReleaseStringUTFChars(hostname, c_hostname);
+ }
+}
+
+static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
+ jint notifId, jint response)
+{
+ if (!sGpsNiInterface) {
+ sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+ }
+ if (sGpsNiInterface) {
+ sGpsNiInterface->respond(notifId, response);
+ }
+}
+
+static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj)
+{
+ jstring result = NULL;
+ if (sGpsDebugInterface) {
+ const size_t maxLength = 2047;
+ char buffer[maxLength+1];
+ size_t length = sGpsDebugInterface->get_internal_state(buffer, maxLength);
+ if (length > maxLength) length = maxLength;
+ buffer[length] = 0;
+ result = env->NewStringUTF(buffer);
+ }
+ return result;
+}
+
+static JNINativeMethod sMethods[] = {
+ /* name, signature, funcPtr */
+ {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
+ {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
+ {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},
+ {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup},
+ {"native_set_position_mode", "(IIIII)Z", (void*)android_location_GpsLocationProvider_set_position_mode},
+ {"native_start", "()Z", (void*)android_location_GpsLocationProvider_start},
+ {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop},
+ {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data},
+ {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status},
+ {"native_read_nmea", "([BI)I", (void*)android_location_GpsLocationProvider_read_nmea},
+ {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},
+ {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location},
+ {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra},
+ {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data},
+ {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open},
+ {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed},
+ {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed},
+ {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server},
+ {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response},
+ {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state},
+};
+
+int register_android_server_location_GpsLocationProvider(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, "com/android/server/location/GpsLocationProvider", sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index c16fdb8..cd4f0a4 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -6,11 +6,12 @@
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryService(JNIEnv* env);
-int register_android_server_KeyInputQueue(JNIEnv* env);
+int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
-int register_android_server_SensorService(JNIEnv* env);
+int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
+int register_android_server_location_GpsLocationProvider(JNIEnv* env);
};
using namespace android;
@@ -26,13 +27,14 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
}
LOG_ASSERT(env, "Could not retrieve the env!");
- register_android_server_KeyInputQueue(env);
+ register_android_server_PowerManagerService(env);
+ register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_BatteryService(env);
- register_android_server_SensorService(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
+ register_android_server_location_GpsLocationProvider(env);
return JNI_VERSION_1_4;
}
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
new file mode 100644
index 0000000..75f690f
--- /dev/null
+++ b/services/sensorservice/Android.mk
@@ -0,0 +1,28 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ SensorService.cpp
+
+LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
+
+# need "-lrt" on Linux simulator to pick up clock_gettime
+ifeq ($(TARGET_SIMULATOR),true)
+ ifeq ($(HOST_OS),linux)
+ LOCAL_LDLIBS += -lrt -lpthread
+ endif
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libhardware \
+ libutils \
+ libbinder \
+ libui \
+ libgui
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE:= libsensorservice
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
new file mode 100644
index 0000000..3025f77
--- /dev/null
+++ b/services/sensorservice/SensorService.cpp
@@ -0,0 +1,560 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+#include <utils/String16.h>
+
+#include <binder/BinderService.h>
+#include <binder/IServiceManager.h>
+
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+
+#include <hardware/sensors.h>
+
+#include "SensorService.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class BatteryService : public Singleton<BatteryService> {
+ static const int TRANSACTION_noteStartSensor = IBinder::FIRST_CALL_TRANSACTION + 3;
+ static const int TRANSACTION_noteStopSensor = IBinder::FIRST_CALL_TRANSACTION + 4;
+ static const String16 DESCRIPTOR;
+
+ friend class Singleton<BatteryService>;
+ sp<IBinder> mBatteryStatService;
+
+ BatteryService() {
+ const sp<IServiceManager> sm(defaultServiceManager());
+ if (sm != NULL) {
+ const String16 name("batteryinfo");
+ mBatteryStatService = sm->getService(name);
+ }
+ }
+
+ status_t noteStartSensor(int uid, int handle) {
+ Parcel data, reply;
+ data.writeInterfaceToken(DESCRIPTOR);
+ data.writeInt32(uid);
+ data.writeInt32(handle);
+ status_t err = mBatteryStatService->transact(
+ TRANSACTION_noteStartSensor, data, &reply, 0);
+ err = reply.readExceptionCode();
+ return err;
+ }
+
+ status_t noteStopSensor(int uid, int handle) {
+ Parcel data, reply;
+ data.writeInterfaceToken(DESCRIPTOR);
+ data.writeInt32(uid);
+ data.writeInt32(handle);
+ status_t err = mBatteryStatService->transact(
+ TRANSACTION_noteStopSensor, data, &reply, 0);
+ err = reply.readExceptionCode();
+ return err;
+ }
+
+public:
+ void enableSensor(int handle) {
+ if (mBatteryStatService != 0) {
+ int uid = IPCThreadState::self()->getCallingUid();
+ int64_t identity = IPCThreadState::self()->clearCallingIdentity();
+ noteStartSensor(uid, handle);
+ IPCThreadState::self()->restoreCallingIdentity(identity);
+ }
+ }
+ void disableSensor(int handle) {
+ if (mBatteryStatService != 0) {
+ int uid = IPCThreadState::self()->getCallingUid();
+ int64_t identity = IPCThreadState::self()->clearCallingIdentity();
+ noteStopSensor(uid, handle);
+ IPCThreadState::self()->restoreCallingIdentity(identity);
+ }
+ }
+};
+
+const String16 BatteryService::DESCRIPTOR("com.android.internal.app.IBatteryStats");
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BatteryService)
+
+// ---------------------------------------------------------------------------
+
+// 100 events/s max
+static const nsecs_t MINIMUM_EVENT_PERIOD = ms2ns(10);
+
+SensorService::SensorService()
+ : Thread(false),
+ mSensorDevice(0),
+ mSensorModule(0),
+ mDump("android.permission.DUMP"),
+ mInitCheck(NO_INIT)
+{
+}
+
+void SensorService::onFirstRef()
+{
+ LOGD("nuSensorService starting...");
+
+ status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&mSensorModule);
+
+ LOGE_IF(err, "couldn't load %s module (%s)",
+ SENSORS_HARDWARE_MODULE_ID, strerror(-err));
+
+ if (mSensorModule) {
+ err = sensors_open(&mSensorModule->common, &mSensorDevice);
+
+ LOGE_IF(err, "couldn't open device for module %s (%s)",
+ SENSORS_HARDWARE_MODULE_ID, strerror(-err));
+
+ sensors_event_t event;
+ memset(&event, 0, sizeof(event));
+
+ struct sensor_t const* list;
+ int count = mSensorModule->get_sensors_list(mSensorModule, &list);
+ mLastEventSeen.setCapacity(count);
+ for (int i=0 ; i<count ; i++) {
+ Sensor sensor(list + i);
+ LOGI("%s", sensor.getName().string());
+ mSensorList.add(sensor);
+ if (mSensorDevice) {
+ mSensorDevice->activate(mSensorDevice, sensor.getHandle(), 0);
+ }
+ mLastEventSeen.add(sensor.getHandle(), event);
+ }
+
+ if (mSensorDevice) {
+ run("SensorService", PRIORITY_URGENT_DISPLAY);
+ mInitCheck = NO_ERROR;
+ }
+ }
+}
+
+SensorService::~SensorService()
+{
+}
+
+status_t SensorService::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 1024;
+ char buffer[SIZE];
+ String8 result;
+ if (!mDump.checkCalling()) {
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump SurfaceFlinger from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ } else {
+ Mutex::Autolock _l(mLock);
+ snprintf(buffer, SIZE, "Sensor List:\n");
+ result.append(buffer);
+ for (size_t i=0 ; i<mSensorList.size() ; i++) {
+ const Sensor& s(mSensorList[i]);
+ const sensors_event_t& e(mLastEventSeen.valueFor(s.getHandle()));
+ snprintf(buffer, SIZE, "%s (vendor=%s, handle=%d, last=<%5.1f,%5.1f,%5.1f>)\n",
+ s.getName().string(),
+ s.getVendor().string(),
+ s.getHandle(),
+ e.data[0], e.data[1], e.data[2]);
+ result.append(buffer);
+ }
+
+ snprintf(buffer, SIZE, "%d active connections\n",
+ mActiveConnections.size());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Active sensors:\n");
+ result.append(buffer);
+ for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
+ int handle = mActiveSensors.keyAt(i);
+ snprintf(buffer, SIZE, "%s (handle=%d, connections=%d)\n",
+ getSensorName(handle).string(),
+ handle,
+ mActiveSensors.valueAt(i)->getNumConnections());
+ result.append(buffer);
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+bool SensorService::threadLoop()
+{
+ LOGD("nuSensorService thread starting...");
+
+ sensors_event_t buffer[16];
+ sensors_event_t scratch[16];
+ struct sensors_poll_device_t* device = mSensorDevice;
+ ssize_t count;
+
+ do {
+ count = device->poll(device, buffer, sizeof(buffer)/sizeof(*buffer));
+ if (count<0) {
+ LOGE("sensor poll failed (%s)", strerror(-count));
+ break;
+ }
+
+ const SortedVector< wp<SensorEventConnection> > activeConnections(
+ getActiveConnections());
+
+ size_t numConnections = activeConnections.size();
+ if (numConnections) {
+ Mutex::Autolock _l(mLock);
+
+ // record the last event for each sensor
+ int32_t prev = buffer[0].sensor;
+ for (ssize_t i=1 ; i<count ; i++) {
+ // record the last event of each sensor type in this buffer
+ int32_t curr = buffer[i].sensor;
+ if (curr != prev) {
+ mLastEventSeen.editValueFor(prev) = buffer[i-1];
+ prev = curr;
+ }
+ }
+ mLastEventSeen.editValueFor(prev) = buffer[count-1];
+
+ for (size_t i=0 ; i<numConnections ; i++) {
+ sp<SensorEventConnection> connection(activeConnections[i].promote());
+ if (connection != 0) {
+ connection->sendEvents(buffer, count, scratch);
+ }
+ }
+ }
+
+ } while (count >= 0 || Thread::exitPending());
+
+ LOGW("Exiting SensorService::threadLoop!");
+ return false;
+}
+
+SortedVector< wp<SensorService::SensorEventConnection> >
+SensorService::getActiveConnections() const
+{
+ Mutex::Autolock _l(mLock);
+ return mActiveConnections;
+}
+
+String8 SensorService::getSensorName(int handle) const {
+ size_t count = mSensorList.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const Sensor& sensor(mSensorList[i]);
+ if (sensor.getHandle() == handle) {
+ return sensor.getName();
+ }
+ }
+ String8 result("unknown");
+ return result;
+}
+
+Vector<Sensor> SensorService::getSensorList()
+{
+ return mSensorList;
+}
+
+sp<ISensorEventConnection> SensorService::createSensorEventConnection()
+{
+ sp<SensorEventConnection> result(new SensorEventConnection(this));
+ return result;
+}
+
+void SensorService::cleanupConnection(const wp<SensorEventConnection>& connection)
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mActiveSensors.size();
+ for (size_t i=0 ; i<size ; ) {
+ SensorRecord* rec = mActiveSensors.valueAt(i);
+ if (rec && rec->removeConnection(connection)) {
+ mSensorDevice->activate(mSensorDevice, mActiveSensors.keyAt(i), 0);
+ mActiveSensors.removeItemsAt(i, 1);
+ delete rec;
+ size--;
+ } else {
+ i++;
+ }
+ }
+ mActiveConnections.remove(connection);
+}
+
+status_t SensorService::enable(const sp<SensorEventConnection>& connection,
+ int handle)
+{
+ if (mInitCheck != NO_ERROR)
+ return mInitCheck;
+
+ status_t err = NO_ERROR;
+ Mutex::Autolock _l(mLock);
+ SensorRecord* rec = mActiveSensors.valueFor(handle);
+ if (rec == 0) {
+ rec = new SensorRecord(connection);
+ mActiveSensors.add(handle, rec);
+ err = mSensorDevice->activate(mSensorDevice, handle, 1);
+ LOGE_IF(err, "Error activating sensor %d (%s)", handle, strerror(-err));
+ if (err == 0) {
+ BatteryService::getInstance().enableSensor(handle);
+ }
+ } else {
+ if (rec->addConnection(connection)) {
+ // this sensor is already activated, but we are adding a
+ // connection that uses it. Immediately send down the last
+ // known value of the requested sensor.
+ sensors_event_t scratch;
+ sensors_event_t& event(mLastEventSeen.editValueFor(handle));
+ if (event.version == sizeof(sensors_event_t)) {
+ connection->sendEvents(&event, 1);
+ }
+ }
+ }
+ if (err == NO_ERROR) {
+ // connection now active
+ if (connection->addSensor(handle)) {
+ // the sensor was added (which means it wasn't already there)
+ // so, see if this connection becomes active
+ if (mActiveConnections.indexOf(connection) < 0) {
+ mActiveConnections.add(connection);
+ }
+ // this could change the sensor event delivery speed
+ recomputeEventsPeriodLocked(handle);
+ }
+ }
+ return err;
+}
+
+status_t SensorService::disable(const sp<SensorEventConnection>& connection,
+ int handle)
+{
+ if (mInitCheck != NO_ERROR)
+ return mInitCheck;
+
+ status_t err = NO_ERROR;
+ Mutex::Autolock _l(mLock);
+ SensorRecord* rec = mActiveSensors.valueFor(handle);
+ if (rec) {
+ // see if this connection becomes inactive
+ connection->removeSensor(handle);
+ if (connection->hasAnySensor() == false) {
+ mActiveConnections.remove(connection);
+ }
+ // see if this sensor becomes inactive
+ if (rec->removeConnection(connection)) {
+ mActiveSensors.removeItem(handle);
+ delete rec;
+ err = mSensorDevice->activate(mSensorDevice, handle, 0);
+ if (err == 0) {
+ BatteryService::getInstance().disableSensor(handle);
+ }
+ }
+ }
+ if (err == NO_ERROR) {
+ recomputeEventsPeriodLocked(handle);
+ }
+ return err;
+}
+
+status_t SensorService::setEventRate(const sp<SensorEventConnection>& connection,
+ int handle, nsecs_t ns)
+{
+ if (mInitCheck != NO_ERROR)
+ return mInitCheck;
+
+ if (ns < 0)
+ return BAD_VALUE;
+
+ if (ns < MINIMUM_EVENTS_PERIOD)
+ ns = MINIMUM_EVENTS_PERIOD;
+
+ Mutex::Autolock _l(mLock);
+ status_t err = connection->setEventRateLocked(handle, ns);
+ if (err == NO_ERROR) {
+ recomputeEventsPeriodLocked(handle);
+ }
+ return err;
+}
+
+status_t SensorService::recomputeEventsPeriodLocked(int32_t handle)
+{
+ status_t err = NO_ERROR;
+ nsecs_t wanted = ms2ns(1000);
+ size_t count = mActiveConnections.size();
+ for (size_t i=0 ; i<count ; i++) {
+ sp<SensorEventConnection> connection(mActiveConnections[i].promote());
+ if (connection != NULL) {
+ nsecs_t ns = connection->getEventRateForSensor(handle);
+ if (ns) {
+ wanted = wanted < ns ? wanted : ns;
+ }
+ }
+ }
+ err = mSensorDevice->setDelay(mSensorDevice, handle, wanted);
+ return err;
+}
+
+// ---------------------------------------------------------------------------
+
+SensorService::SensorRecord::SensorRecord(
+ const sp<SensorEventConnection>& connection)
+{
+ mConnections.add(connection);
+}
+
+bool SensorService::SensorRecord::addConnection(
+ const sp<SensorEventConnection>& connection)
+{
+ if (mConnections.indexOf(connection) < 0) {
+ mConnections.add(connection);
+ return true;
+ }
+ return false;
+}
+
+bool SensorService::SensorRecord::removeConnection(
+ const wp<SensorEventConnection>& connection)
+{
+ ssize_t index = mConnections.indexOf(connection);
+ if (index >= 0) {
+ mConnections.removeItemsAt(index, 1);
+ }
+ return mConnections.size() ? false : true;
+}
+
+// ---------------------------------------------------------------------------
+
+SensorService::SensorEventConnection::SensorEventConnection(
+ const sp<SensorService>& service)
+ : mService(service), mChannel(new SensorChannel())
+{
+}
+
+SensorService::SensorEventConnection::~SensorEventConnection()
+{
+ mService->cleanupConnection(this);
+}
+
+void SensorService::SensorEventConnection::onFirstRef()
+{
+}
+
+bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
+ if (mSensorInfo.indexOfKey(handle) <= 0) {
+ SensorInfo info;
+ mSensorInfo.add(handle, info);
+ return true;
+ }
+ return false;
+}
+
+bool SensorService::SensorEventConnection::removeSensor(int32_t handle) {
+ if (mSensorInfo.removeItem(handle) >= 0) {
+ return true;
+ }
+ return false;
+}
+
+bool SensorService::SensorEventConnection::hasSensor(int32_t handle) const {
+ return mSensorInfo.indexOfKey(handle) >= 0;
+}
+
+bool SensorService::SensorEventConnection::hasAnySensor() const {
+ return mSensorInfo.size() ? true : false;
+}
+
+status_t SensorService::SensorEventConnection::setEventRateLocked(
+ int handle, nsecs_t ns)
+{
+ ssize_t index = mSensorInfo.indexOfKey(handle);
+ if (index >= 0) {
+ SensorInfo& info = mSensorInfo.editValueFor(handle);
+ info.ns = ns;
+ return NO_ERROR;
+ }
+ return status_t(index);
+}
+
+status_t SensorService::SensorEventConnection::sendEvents(
+ sensors_event_t const* buffer, size_t numEvents,
+ sensors_event_t* scratch)
+{
+ // filter out events not for this connection
+ size_t count = 0;
+ if (scratch) {
+ size_t i=0;
+ while (i<numEvents) {
+ const int32_t curr = buffer[i].sensor;
+ if (mSensorInfo.indexOfKey(curr) >= 0) {
+ do {
+ scratch[count++] = buffer[i++];
+ } while ((i<numEvents) && (buffer[i].sensor == curr));
+ } else {
+ i++;
+ }
+ }
+ } else {
+ scratch = const_cast<sensors_event_t *>(buffer);
+ count = numEvents;
+ }
+
+ if (count == 0)
+ return 0;
+
+ ssize_t size = mChannel->write(scratch, count*sizeof(sensors_event_t));
+ if (size == -EAGAIN) {
+ // the destination doesn't accept events anymore, it's probably
+ // full. For now, we just drop the events on the floor.
+ LOGW("dropping %d events on the floor", count);
+ return size;
+ }
+
+ LOGE_IF(size<0, "dropping %d events on the floor (%s)",
+ count, strerror(-size));
+
+ return size < 0 ? size : NO_ERROR;
+}
+
+sp<SensorChannel> SensorService::SensorEventConnection::getSensorChannel() const
+{
+ return mChannel;
+}
+
+status_t SensorService::SensorEventConnection::enableDisable(
+ int handle, bool enabled)
+{
+ status_t err;
+ if (enabled) {
+ err = mService->enable(this, handle);
+ } else {
+ err = mService->disable(this, handle);
+ }
+ return err;
+}
+
+status_t SensorService::SensorEventConnection::setEventRate(
+ int handle, nsecs_t ns)
+{
+ return mService->setEventRate(this, handle, ns);
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
new file mode 100644
index 0000000..9f37799
--- /dev/null
+++ b/services/sensorservice/SensorService.h
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SENSOR_SERVICE_H
+#define ANDROID_SENSOR_SERVICE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Vector.h>
+#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/RefBase.h>
+
+#include <binder/BinderService.h>
+#include <binder/Permission.h>
+
+#include <gui/Sensor.h>
+#include <gui/SensorChannel.h>
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+
+// ---------------------------------------------------------------------------
+
+struct sensors_poll_device_t;
+struct sensors_module_t;
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class SensorService :
+ public BinderService<SensorService>,
+ public BnSensorServer,
+ protected Thread
+{
+ friend class BinderService<SensorService>;
+
+ static const nsecs_t MINIMUM_EVENTS_PERIOD = 10000000; // 10ms
+ static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; // 200 ms
+
+ SensorService();
+ virtual ~SensorService();
+
+ virtual void onFirstRef();
+
+ // Thread interface
+ virtual bool threadLoop();
+
+ // ISensorServer interface
+ virtual Vector<Sensor> getSensorList();
+ virtual sp<ISensorEventConnection> createSensorEventConnection();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+
+ class SensorEventConnection : public BnSensorEventConnection {
+ virtual ~SensorEventConnection();
+ virtual void onFirstRef();
+ virtual sp<SensorChannel> 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;
+
+ // protected by SensorService::mLock
+ struct SensorInfo {
+ SensorInfo() : ns(DEFAULT_EVENTS_PERIOD) { }
+ nsecs_t ns;
+ };
+ DefaultKeyedVector<int32_t, SensorInfo> mSensorInfo;
+
+ public:
+ SensorEventConnection(const sp<SensorService>& service);
+
+ status_t sendEvents(sensors_event_t const* buffer, size_t count,
+ sensors_event_t* scratch = NULL);
+ bool hasSensor(int32_t handle) const;
+ bool hasAnySensor() const;
+ bool addSensor(int32_t handle);
+ bool removeSensor(int32_t handle);
+ status_t setEventRateLocked(int handle, nsecs_t ns);
+ nsecs_t getEventRateForSensor(int32_t handle) const {
+ return mSensorInfo.valueFor(handle).ns;
+ }
+ };
+
+ class SensorRecord {
+ SortedVector< wp<SensorEventConnection> > mConnections;
+ public:
+ SensorRecord(const sp<SensorEventConnection>& connection);
+ bool addConnection(const sp<SensorEventConnection>& connection);
+ bool removeConnection(const wp<SensorEventConnection>& connection);
+ size_t getNumConnections() const { return mConnections.size(); }
+ };
+
+ SortedVector< wp<SensorEventConnection> > getActiveConnections() const;
+ String8 getSensorName(int handle) const;
+ status_t recomputeEventsPeriodLocked(int32_t handle);
+
+ // constants
+ Vector<Sensor> mSensorList;
+ struct sensors_poll_device_t* mSensorDevice;
+ struct sensors_module_t* mSensorModule;
+ Permission mDump;
+ status_t mInitCheck;
+
+ // protected by mLock
+ mutable Mutex mLock;
+ DefaultKeyedVector<int, SensorRecord*> mActiveSensors;
+ SortedVector< wp<SensorEventConnection> > mActiveConnections;
+
+ // The size of this vector is constant, only the items are mutable
+ KeyedVector<int32_t, sensors_event_t> mLastEventSeen;
+
+public:
+ static char const* getServiceName() { return "sensorservice"; }
+
+ void cleanupConnection(const wp<SensorEventConnection>& connection);
+ status_t enable(const sp<SensorEventConnection>& connection, int handle);
+ status_t disable(const sp<SensorEventConnection>& connection, int handle);
+ status_t setEventRate(const sp<SensorEventConnection>& connection, int handle, nsecs_t ns);
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SENSOR_SERVICE_H
diff --git a/services/sensorservice/tests/Android.mk b/services/sensorservice/tests/Android.mk
new file mode 100644
index 0000000..45296dd
--- /dev/null
+++ b/services/sensorservice/tests/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ sensorservicetest.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils libutils libui libgui
+
+LOCAL_MODULE:= test-sensorservice
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
new file mode 100644
index 0000000..e464713
--- /dev/null
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <android/sensor.h>
+#include <gui/Sensor.h>
+#include <gui/SensorManager.h>
+#include <gui/SensorEventQueue.h>
+#include <utils/PollLoop.h>
+
+using namespace android;
+
+bool receiver(int fd, int events, void* data)
+{
+ sp<SensorEventQueue> q((SensorEventQueue*)data);
+ ssize_t n;
+ ASensorEvent buffer[8];
+ while ((n = q->read(buffer, 8)) > 0) {
+ for (int i=0 ; i<n ; i++) {
+ if (buffer[i].type == Sensor::TYPE_ACCELEROMETER) {
+ printf("time=%lld, value=<%5.1f,%5.1f,%5.1f>\n",
+ buffer[i].timestamp,
+ buffer[i].acceleration.x,
+ buffer[i].acceleration.y,
+ buffer[i].acceleration.z);
+ }
+ }
+ }
+ if (n<0 && n != -EAGAIN) {
+ printf("error reading events (%s)\n", strerror(-n));
+ }
+ return true;
+}
+
+
+int main(int argc, char** argv)
+{
+ SensorManager& mgr(SensorManager::getInstance());
+
+ Sensor const* const* list;
+ ssize_t count = mgr.getSensorList(&list);
+ printf("numSensors=%d\n", count);
+
+ sp<SensorEventQueue> q = mgr.createEventQueue();
+ printf("queue=%p\n", q.get());
+
+ Sensor const* accelerometer = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER);
+ printf("accelerometer=%p (%s)\n",
+ accelerometer, accelerometer->getName().string());
+ q->enableSensor(accelerometer);
+
+ q->setEventRate(accelerometer, ms2ns(10));
+
+ sp<PollLoop> loop = new PollLoop(false);
+ loop->setCallback(q->getFd(), POLLIN, receiver, q.get());
+
+ do {
+ //printf("about to poll...\n");
+ int32_t ret = loop->pollOnce(-1, 0, 0);
+ switch (ret) {
+ 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/Android.mk b/services/surfaceflinger/Android.mk
index 86eb78d..a14bfb5 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \
DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
BlurFilter.cpp.arm \
+ GLExtensions.cpp \
Layer.cpp \
LayerBase.cpp \
LayerBuffer.cpp \
@@ -13,14 +14,14 @@ LOCAL_SRC_FILES:= \
LayerDim.cpp \
MessageQueue.cpp \
SurfaceFlinger.cpp \
- Tokenizer.cpp \
+ TextureManager.cpp \
Transform.cpp
LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-ifeq ($(TARGET_BOARD_PLATFORM), msm7k)
- LOCAL_CFLAGS += -DDIM_WITH_TEXTURE
+ifeq ($(TARGET_BOARD_PLATFORM), omap3)
+ LOCAL_CFLAGS += -DNO_RGBX_8888
endif
# need "-lrt" on Linux simulator to pick up clock_gettime
diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h
index e2bcf6a..6f8507e 100644
--- a/services/surfaceflinger/Barrier.h
+++ b/services/surfaceflinger/Barrier.h
@@ -29,10 +29,6 @@ public:
inline Barrier() : state(CLOSED) { }
inline ~Barrier() { }
void open() {
- // gcc memory barrier, this makes sure all memory writes
- // have been issued by gcc. On an SMP system we'd need a real
- // h/w barrier.
- asm volatile ("":::"memory");
Mutex::Autolock _l(lock);
state = OPENED;
cv.broadcast();
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index ea68352..2eac0a8 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -40,6 +40,8 @@
#include <hardware/overlay.h>
#include <hardware/gralloc.h>
+#include "GLExtensions.h"
+
using namespace android;
@@ -73,7 +75,8 @@ void checkEGLErrors(const char* token)
DisplayHardware::DisplayHardware(
const sp<SurfaceFlinger>& flinger,
uint32_t dpy)
- : DisplayHardwareBase(flinger, dpy)
+ : DisplayHardwareBase(flinger, dpy),
+ mFlags(0)
{
init(dpy);
}
@@ -97,6 +100,9 @@ void DisplayHardware::init(uint32_t dpy)
{
mNativeWindow = new FramebufferNativeWindow();
framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
+ mDpiX = mNativeWindow->xdpi;
+ mDpiY = mNativeWindow->ydpi;
+ mRefreshRate = fbDev->fps;
mOverlayEngine = NULL;
hw_module_t const* module;
@@ -104,6 +110,11 @@ void DisplayHardware::init(uint32_t dpy)
overlay_control_open(module, &mOverlayEngine);
}
+ EGLint w, h, dummy;
+ EGLint numConfigs=0;
+ EGLSurface surface;
+ EGLContext context;
+
// initialize EGL
EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
@@ -121,12 +132,6 @@ void DisplayHardware::init(uint32_t dpy)
}
}
- EGLint w, h, dummy;
- EGLint numConfigs=0;
- EGLSurface surface;
- EGLContext context;
- mFlags = CACHED_BUFFERS;
-
// TODO: all the extensions below should be queried through
// eglGetProcAddress().
@@ -145,22 +150,6 @@ void DisplayHardware::init(uint32_t dpy)
eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
- /*
- * Gather EGL extensions
- */
-
- const char* const egl_extensions = eglQueryString(
- display, EGL_EXTENSIONS);
-
- LOGI("EGL informations:");
- LOGI("# of configs : %d", numConfigs);
- LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
- LOGI("version : %s", eglQueryString(display, EGL_VERSION));
- LOGI("extensions: %s", egl_extensions);
- LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
- LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
-
-
if (mNativeWindow->isUpdateOnDemand()) {
mFlags |= PARTIAL_UPDATES;
}
@@ -175,6 +164,8 @@ void DisplayHardware::init(uint32_t dpy)
*/
surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
+ eglQuerySurface(display, surface, EGL_WIDTH, &mWidth);
+ eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
if (mFlags & PARTIAL_UPDATES) {
// if we have partial updates, we definitely don't need to
@@ -188,31 +179,6 @@ void DisplayHardware::init(uint32_t dpy)
mFlags |= BUFFER_PRESERVED;
}
}
-
- eglQuerySurface(display, surface, EGL_WIDTH, &mWidth);
- eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
-
-#ifdef EGL_ANDROID_swap_rectangle
- if (strstr(egl_extensions, "EGL_ANDROID_swap_rectangle")) {
- if (eglSetSwapRectangleANDROID(display, surface,
- 0, 0, mWidth, mHeight) == EGL_TRUE) {
- // This could fail if this extension is not supported by this
- // specific surface (of config)
- mFlags |= SWAP_RECTANGLE;
- }
- }
- // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE
- // choose PARTIAL_UPDATES, which should be more efficient
- if (mFlags & PARTIAL_UPDATES)
- mFlags &= ~SWAP_RECTANGLE;
-#endif
-
-
- LOGI("flags : %08x", mFlags);
-
- mDpiX = mNativeWindow->xdpi;
- mDpiY = mNativeWindow->ydpi;
- mRefreshRate = fbDev->fps;
/* Read density from build-specific ro.sf.lcd_density property
* except if it is overridden by qemu.sf.lcd_density.
@@ -235,61 +201,67 @@ void DisplayHardware::init(uint32_t dpy)
context = eglCreateContext(display, config, NULL, NULL);
+ mDisplay = display;
+ mConfig = config;
+ mSurface = surface;
+ mContext = context;
+ mFormat = fbDev->format;
+ mPageFlipCount = 0;
+
/*
* Gather OpenGL ES extensions
*/
eglMakeCurrent(display, surface, surface, context);
- const char* const gl_extensions = (const char*)glGetString(GL_EXTENSIONS);
- const char* const gl_renderer = (const char*)glGetString(GL_RENDERER);
- LOGI("OpenGL informations:");
- LOGI("vendor : %s", glGetString(GL_VENDOR));
- LOGI("renderer : %s", gl_renderer);
- LOGI("version : %s", glGetString(GL_VERSION));
- LOGI("extensions: %s", gl_extensions);
+
+ GLExtensions& extensions(GLExtensions::getInstance());
+ extensions.initWithGLStrings(
+ glGetString(GL_VENDOR),
+ glGetString(GL_RENDERER),
+ glGetString(GL_VERSION),
+ glGetString(GL_EXTENSIONS),
+ eglQueryString(display, EGL_VENDOR),
+ eglQueryString(display, EGL_VERSION),
+ eglQueryString(display, EGL_EXTENSIONS));
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
- LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
- LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
-#if 0
- // for drivers that don't have proper support for flushing cached buffers
- // on gralloc unlock, uncomment this block and test for the specific
- // renderer substring
- if (strstr(gl_renderer, "<some vendor string>")) {
- LOGD("Assuming uncached graphics buffers.");
- mFlags &= ~CACHED_BUFFERS;
- }
-#endif
- if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) {
- mFlags |= NPOT_EXTENSION;
- }
- if (strstr(gl_extensions, "GL_OES_draw_texture")) {
- mFlags |= DRAW_TEXTURE_EXTENSION;
- }
-#ifdef EGL_ANDROID_image_native_buffer
- if (strstr( gl_extensions, "GL_OES_EGL_image") &&
- (strstr(egl_extensions, "EGL_KHR_image_base") ||
- strstr(egl_extensions, "EGL_KHR_image")) &&
- strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) {
- mFlags |= DIRECT_TEXTURE;
+#ifdef EGL_ANDROID_swap_rectangle
+ if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) {
+ if (eglSetSwapRectangleANDROID(display, surface,
+ 0, 0, mWidth, mHeight) == EGL_TRUE) {
+ // This could fail if this extension is not supported by this
+ // specific surface (of config)
+ mFlags |= SWAP_RECTANGLE;
+ }
}
-#else
-#warning "EGL_ANDROID_image_native_buffer not supported"
+ // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE
+ // choose PARTIAL_UPDATES, which should be more efficient
+ if (mFlags & PARTIAL_UPDATES)
+ mFlags &= ~SWAP_RECTANGLE;
#endif
+ LOGI("EGL informations:");
+ LOGI("# of configs : %d", numConfigs);
+ LOGI("vendor : %s", extensions.getEglVendor());
+ LOGI("version : %s", extensions.getEglVersion());
+ LOGI("extensions: %s", extensions.getEglExtension());
+ LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
+ LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+
+ LOGI("OpenGL informations:");
+ LOGI("vendor : %s", extensions.getVendor());
+ LOGI("renderer : %s", extensions.getRenderer());
+ LOGI("version : %s", extensions.getVersion());
+ LOGI("extensions: %s", extensions.getExtension());
+ LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
+ LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
+ LOGI("flags = %08x", mFlags);
// Unbind the context from this thread
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
- mDisplay = display;
- mConfig = config;
- mSurface = surface;
- mContext = context;
- mFormat = fbDev->format;
- mPageFlipCount = 0;
}
/*
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index df046af..66bf521 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -29,6 +29,8 @@
#include <pixelflinger/pixelflinger.h>
+#include "GLExtensions.h"
+
#include "DisplayHardware/DisplayHardwareBase.h"
struct overlay_control_device_t;
@@ -43,15 +45,11 @@ class DisplayHardware : public DisplayHardwareBase
{
public:
enum {
- DIRECT_TEXTURE = 0x00000002,
- COPY_BITS_EXTENSION = 0x00000008,
- NPOT_EXTENSION = 0x00000100,
- DRAW_TEXTURE_EXTENSION = 0x00000200,
- BUFFER_PRESERVED = 0x00010000,
- PARTIAL_UPDATES = 0x00020000, // video driver feature
- SLOW_CONFIG = 0x00040000, // software
- SWAP_RECTANGLE = 0x00080000,
- CACHED_BUFFERS = 0x00100000
+ COPY_BITS_EXTENSION = 0x00000008,
+ BUFFER_PRESERVED = 0x00010000,
+ PARTIAL_UPDATES = 0x00020000, // video driver feature
+ SLOW_CONFIG = 0x00040000, // software
+ SWAP_RECTANGLE = 0x00080000,
};
DisplayHardware(
diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
new file mode 100644
index 0000000..7f4f9fc
--- /dev/null
+++ b/services/surfaceflinger/GLExtensions.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "GLExtensions.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions )
+
+GLExtensions::GLExtensions()
+ : mHaveTextureExternal(false),
+ mHaveNpot(false),
+ mHaveDirectTexture(false)
+{
+}
+
+void GLExtensions::initWithGLStrings(
+ GLubyte const* vendor,
+ GLubyte const* renderer,
+ GLubyte const* version,
+ GLubyte const* extensions,
+ char const* egl_vendor,
+ char const* egl_version,
+ char const* egl_extensions)
+{
+ mVendor = (char const*)vendor;
+ mRenderer = (char const*)renderer;
+ mVersion = (char const*)version;
+ mExtensions = (char const*)extensions;
+ mEglVendor = egl_vendor;
+ mEglVersion = egl_version;
+ mEglExtensions = egl_extensions;
+
+ char const* curr = (char const*)extensions;
+ char const* head = curr;
+ do {
+ head = strchr(curr, ' ');
+ String8 s(curr, head ? head-curr : strlen(curr));
+ if (s.length()) {
+ mExtensionList.add(s);
+ }
+ curr = head+1;
+ } while (head);
+
+ curr = egl_extensions;
+ head = curr;
+ do {
+ head = strchr(curr, ' ');
+ String8 s(curr, head ? head-curr : strlen(curr));
+ if (s.length()) {
+ mExtensionList.add(s);
+ }
+ curr = head+1;
+ } while (head);
+
+#ifdef EGL_ANDROID_image_native_buffer
+ if (hasExtension("GL_OES_EGL_image") &&
+ (hasExtension("EGL_KHR_image_base") || hasExtension("EGL_KHR_image")) &&
+ hasExtension("EGL_ANDROID_image_native_buffer"))
+ {
+ mHaveDirectTexture = true;
+ }
+#else
+#warning "EGL_ANDROID_image_native_buffer not supported"
+#endif
+
+ if (hasExtension("GL_ARB_texture_non_power_of_two")) {
+ mHaveNpot = true;
+ }
+
+ if (hasExtension("GL_OES_texture_external")) {
+ mHaveTextureExternal = true;
+ } else if (strstr(mRenderer.string(), "Adreno")) {
+ // hack for Adreno 200
+ mHaveTextureExternal = true;
+ }
+}
+
+bool GLExtensions::hasExtension(char const* extension) const
+{
+ const String8 s(extension);
+ return mExtensionList.indexOf(s) >= 0;
+}
+
+char const* GLExtensions::getVendor() const {
+ return mVendor.string();
+}
+
+char const* GLExtensions::getRenderer() const {
+ return mRenderer.string();
+}
+
+char const* GLExtensions::getVersion() const {
+ return mVersion.string();
+}
+
+char const* GLExtensions::getExtension() const {
+ return mExtensions.string();
+}
+
+char const* GLExtensions::getEglVendor() const {
+ return mEglVendor.string();
+}
+
+char const* GLExtensions::getEglVersion() const {
+ return mEglVersion.string();
+}
+
+char const* GLExtensions::getEglExtension() const {
+ return mEglExtensions.string();
+}
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h
new file mode 100644
index 0000000..bbb284e
--- /dev/null
+++ b/services/surfaceflinger/GLExtensions.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SF_GLEXTENSION_H
+#define ANDROID_SF_GLEXTENSION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/String8.h>
+#include <utils/SortedVector.h>
+#include <utils/Singleton.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class GLExtensions : public Singleton<GLExtensions>
+{
+ friend class Singleton<GLExtensions>;
+
+ bool mHaveTextureExternal : 1;
+ bool mHaveNpot : 1;
+ bool mHaveDirectTexture : 1;
+
+ String8 mVendor;
+ String8 mRenderer;
+ String8 mVersion;
+ String8 mExtensions;
+ String8 mEglVendor;
+ String8 mEglVersion;
+ String8 mEglExtensions;
+ SortedVector<String8> mExtensionList;
+
+ GLExtensions(const GLExtensions&);
+ GLExtensions& operator = (const GLExtensions&);
+
+protected:
+ GLExtensions();
+
+public:
+ inline bool haveTextureExternal() const {
+ return mHaveTextureExternal;
+ }
+ inline bool haveNpot() const {
+ return mHaveNpot;
+ }
+ inline bool haveDirectTexture() const {
+ return mHaveDirectTexture;
+ }
+
+ void initWithGLStrings(
+ GLubyte const* vendor,
+ GLubyte const* renderer,
+ GLubyte const* version,
+ GLubyte const* extensions,
+ char const* egl_vendor,
+ char const* egl_version,
+ char const* egl_extensions);
+
+ char const* getVendor() const;
+ char const* getRenderer() const;
+ char const* getVersion() const;
+ char const* getExtension() const;
+
+ char const* getEglVendor() const;
+ char const* getEglVersion() const;
+ char const* getEglExtension() const;
+
+ bool hasExtension(char const* extension) const;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_GLEXTENSION_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ce7e9aa..695cbfa 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -31,6 +31,7 @@
#include <surfaceflinger/Surface.h>
#include "clz.h"
+#include "GLExtensions.h"
#include "Layer.h"
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
@@ -47,47 +48,73 @@ template <typename T> inline T min(T a, T b) {
// ---------------------------------------------------------------------------
-const uint32_t Layer::typeInfo = LayerBaseClient::typeInfo | 4;
-const char* const Layer::typeID = "Layer";
-
-// ---------------------------------------------------------------------------
-
-Layer::Layer(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& c, int32_t i)
- : LayerBaseClient(flinger, display, c, i),
- mSecure(false),
- mNoEGLImageForSwBuffers(false),
+Layer::Layer(SurfaceFlinger* flinger,
+ DisplayID display, const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client),
+ mGLExtensions(GLExtensions::getInstance()),
mNeedsBlending(true),
- mNeedsDithering(false)
+ mNeedsDithering(false),
+ mSecure(false),
+ mTextureManager(),
+ mBufferManager(mTextureManager),
+ mWidth(0), mHeight(0), mFixedSize(false)
{
- // no OpenGL operation is possible here, since we might not be
- // in the OpenGL thread.
- mFrontBufferIndex = lcblk->getFrontBuffer();
}
Layer::~Layer()
{
- destroy();
- // the actual buffers will be destroyed here
+ // FIXME: must be called from the main UI thread
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ mBufferManager.destroy(dpy);
+
+ // we can use getUserClientUnsafe here because we know we're
+ // single-threaded at that point.
+ sp<UserClient> ourClient(mUserClientRef.getUserClientUnsafe());
+ if (ourClient != 0) {
+ ourClient->detachLayer(this);
+ }
}
-void Layer::destroy()
+status_t Layer::setToken(const sp<UserClient>& userClient,
+ SharedClient* sharedClient, int32_t token)
{
- for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
- if (mTextures[i].name != -1U) {
- glDeleteTextures(1, &mTextures[i].name);
- mTextures[i].name = -1U;
- }
- if (mTextures[i].image != EGL_NO_IMAGE_KHR) {
- EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
- eglDestroyImageKHR(dpy, mTextures[i].image);
- mTextures[i].image = EGL_NO_IMAGE_KHR;
- }
- Mutex::Autolock _l(mLock);
- mBuffers[i].clear();
- mWidth = mHeight = 0;
+ sp<SharedBufferServer> lcblk = new SharedBufferServer(
+ sharedClient, token, mBufferManager.getDefaultBufferCount(),
+ getIdentity());
+
+ status_t err = mUserClientRef.setToken(userClient, lcblk, token);
+
+ LOGE_IF(err != NO_ERROR,
+ "ClientRef::setToken(%p, %p, %u) failed",
+ userClient.get(), lcblk.get(), token);
+
+ if (err == NO_ERROR) {
+ // we need to free the buffers associated with this surface
+ }
+
+ return err;
+}
+
+int32_t Layer::getToken() const
+{
+ return mUserClientRef.getToken();
+}
+
+sp<UserClient> Layer::getClient() const
+{
+ return mUserClientRef.getClient();
+}
+
+// called with SurfaceFlinger::mStateLock as soon as the layer is entered
+// in the purgatory list
+void Layer::onRemoved()
+{
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ // wake up the condition
+ lcblk->setStatus(NO_INIT);
}
- mSurface.clear();
}
sp<LayerBaseClient::Surface> Layer::createSurface() const
@@ -97,9 +124,17 @@ sp<LayerBaseClient::Surface> Layer::createSurface() const
status_t Layer::ditch()
{
+ // NOTE: Called from the main UI thread
+
// the layer is not on screen anymore. free as much resources as possible
mFreezeLock.clear();
- destroy();
+
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ mBufferManager.destroy(dpy);
+ mSurface.clear();
+
+ Mutex::Autolock _l(mLock);
+ mWidth = mHeight = 0;
return NO_ERROR;
}
@@ -129,26 +164,26 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h,
mFormat = format;
mWidth = w;
mHeight = h;
+
+ mReqFormat = format;
+ mReqWidth = w;
+ mReqHeight = h;
+
mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
- mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS);
// we use the red index
int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED);
int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED);
mNeedsDithering = layerRedsize > displayRedSize;
- for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
- mBuffers[i] = new GraphicBuffer();
- }
- mSurface = new SurfaceLayer(mFlinger, clientIndex(), this);
+ mSurface = new SurfaceLayer(mFlinger, this);
return NO_ERROR;
}
void Layer::reloadTexture(const Region& dirty)
{
- Mutex::Autolock _l(mLock);
- sp<GraphicBuffer> buffer(getFrontBufferLocked());
+ sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
if (buffer == NULL) {
// this situation can happen if we ran out of memory for instance.
// not much we can do. continue to use whatever texture was bound
@@ -156,130 +191,33 @@ void Layer::reloadTexture(const Region& dirty)
return;
}
- const int index = mFrontBufferIndex;
-
- // create the new texture name if needed
- if (UNLIKELY(mTextures[index].name == -1U)) {
- mTextures[index].name = createTexture();
- mTextures[index].width = 0;
- mTextures[index].height = 0;
- }
-
-#ifdef EGL_ANDROID_image_native_buffer
- if (mFlags & DisplayHardware::DIRECT_TEXTURE) {
- if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) {
- if (mTextures[index].dirty) {
- if (initializeEglImage(buffer, &mTextures[index]) != NO_ERROR) {
- // not sure what we can do here...
- mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
- goto slowpath;
- }
- }
- } else {
- if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width ||
- mHybridBuffer->height != buffer->height)) {
- mHybridBuffer.clear();
- mHybridBuffer = new GraphicBuffer(
- buffer->width, buffer->height, buffer->format,
- GraphicBuffer::USAGE_SW_WRITE_OFTEN |
- GraphicBuffer::USAGE_HW_TEXTURE);
- if (initializeEglImage(
- mHybridBuffer, &mTextures[0]) != NO_ERROR) {
- // not sure what we can do here...
- mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
- mHybridBuffer.clear();
- goto slowpath;
- }
- }
-
- GGLSurface t;
+ if (mGLExtensions.haveDirectTexture()) {
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) {
+ // not sure what we can do here...
+ goto slowpath;
+ }
+ } else {
+slowpath:
+ GGLSurface t;
+ if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) {
status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
LOGE_IF(res, "error %d (%s) locking buffer %p",
res, strerror(res), buffer.get());
if (res == NO_ERROR) {
- Texture* const texture(&mTextures[0]);
-
- glBindTexture(GL_TEXTURE_2D, texture->name);
-
- sp<GraphicBuffer> buf(mHybridBuffer);
- void* vaddr;
- res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr);
- if (res == NO_ERROR) {
- int bpp = 0;
- switch (t.format) {
- case HAL_PIXEL_FORMAT_RGB_565:
- case HAL_PIXEL_FORMAT_RGBA_4444:
- bpp = 2;
- break;
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- bpp = 4;
- break;
- default:
- if (isSupportedYuvFormat(t.format)) {
- // just show the Y plane of YUV buffers
- bpp = 1;
- break;
- }
- // oops, we don't handle this format!
- LOGE("layer %p, texture=%d, using format %d, which is not "
- "supported by the GL", this, texture->name, t.format);
- }
- if (bpp) {
- const Rect bounds(dirty.getBounds());
- size_t src_stride = t.stride;
- size_t dst_stride = buf->stride;
- if (src_stride == dst_stride &&
- bounds.width() == t.width &&
- bounds.height() == t.height)
- {
- memcpy(vaddr, t.data, t.height * t.stride * bpp);
- } else {
- GLubyte const * src = t.data +
- (bounds.left + bounds.top * src_stride) * bpp;
- GLubyte * dst = (GLubyte *)vaddr +
- (bounds.left + bounds.top * dst_stride) * bpp;
- const size_t length = bounds.width() * bpp;
- size_t h = bounds.height();
- src_stride *= bpp;
- dst_stride *= bpp;
- while (h--) {
- memcpy(dst, src, length);
- dst += dst_stride;
- src += src_stride;
- }
- }
- }
- buf->unlock();
- }
+ mBufferManager.loadTexture(dirty, t);
buffer->unlock();
}
- }
- } else
-#endif
- {
-slowpath:
- for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
- mTextures[i].image = EGL_NO_IMAGE_KHR;
- }
- GGLSurface t;
- status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
- LOGE_IF(res, "error %d (%s) locking buffer %p",
- res, strerror(res), buffer.get());
- if (res == NO_ERROR) {
- loadTexture(&mTextures[0], dirty, t);
- buffer->unlock();
+ } else {
+ // we can't do anything
}
}
}
void Layer::onDraw(const Region& clip) const
{
- int index = mFrontBufferIndex;
- if (mTextures[index].image == EGL_NO_IMAGE_KHR)
- index = 0;
- GLuint textureName = mTextures[index].name;
- if (UNLIKELY(textureName == -1LU)) {
+ Texture tex(mBufferManager.getActiveTexture());
+ if (tex.name == -1LU) {
// the texture has not been created yet, this Layer has
// in fact never been drawn into. This happens frequently with
// SurfaceView because the WindowManager can't know when the client
@@ -301,21 +239,61 @@ void Layer::onDraw(const Region& clip) const
// if not everything below us is covered, we plug the holes!
Region holes(clip.subtract(under));
if (!holes.isEmpty()) {
- clearWithOpenGL(holes);
+ clearWithOpenGL(holes, 0, 0, 0, 1);
}
return;
}
- drawWithOpenGL(clip, mTextures[index]);
+ drawWithOpenGL(clip, tex);
}
-sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
+bool Layer::needsFiltering() const
+{
+ if (!(mFlags & DisplayHardware::SLOW_CONFIG)) {
+ // NOTE: there is a race here, because mFixedSize is updated in a
+ // binder transaction. however, it doesn't really matter since it is
+ // evaluated each time we draw. To be perfectly correct, this flag
+ // would have to be associated with a buffer.
+ if (mFixedSize)
+ return true;
+ }
+ return LayerBase::needsFiltering();
+}
+
+
+status_t Layer::setBufferCount(int bufferCount)
+{
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
+ // oops, the client is already gone
+ return DEAD_OBJECT;
+ }
+
+ // NOTE: lcblk->resize() is protected by an internal lock
+ status_t err = lcblk->resize(bufferCount);
+ if (err == NO_ERROR)
+ mBufferManager.resize(bufferCount);
+
+ return err;
+}
+
+sp<GraphicBuffer> Layer::requestBuffer(int index,
+ uint32_t reqWidth, uint32_t reqHeight, uint32_t reqFormat,
+ uint32_t usage)
{
sp<GraphicBuffer> buffer;
+ if (int32_t(reqWidth | reqHeight | reqFormat) < 0)
+ return buffer;
+
+ if ((!reqWidth && reqHeight) || (reqWidth && !reqHeight))
+ return buffer;
+
// this ensures our client doesn't go away while we're accessing
// the shared area.
- sp<Client> ourClient(client.promote());
- if (ourClient == 0) {
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
// oops, the client is already gone
return buffer;
}
@@ -324,46 +302,38 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
* This is called from the client's Surface::dequeue(). This can happen
* at any time, especially while we're in the middle of using the
* buffer 'index' as our front buffer.
- *
- * Make sure the buffer we're resizing is not the front buffer and has been
- * dequeued. Once this condition is asserted, we are guaranteed that this
- * buffer cannot become the front buffer under our feet, since we're called
- * from Surface::dequeue()
*/
- status_t err = lcblk->assertReallocate(index);
- LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err));
- if (err != NO_ERROR) {
- // the surface may have died
- return buffer;
- }
- uint32_t w, h;
+ status_t err = NO_ERROR;
+ uint32_t w, h, f;
{ // scope for the lock
Mutex::Autolock _l(mLock);
- w = mWidth;
- h = mHeight;
- buffer = mBuffers[index];
-
- // destroy() could have been called before we get here, we log it
- // because it's uncommon, and the code below should handle it
- LOGW_IF(buffer==0,
- "mBuffers[%d] is null (mWidth=%d, mHeight=%d)",
- index, w, h);
-
- mBuffers[index].clear();
+
+ // zero means default
+ if (!reqFormat) reqFormat = mFormat;
+ if (!reqWidth) reqWidth = mWidth;
+ if (!reqHeight) reqHeight = mHeight;
+
+ w = reqWidth;
+ h = reqHeight;
+ f = reqFormat;
+
+ if ((reqWidth != mReqWidth) || (reqHeight != mReqHeight) ||
+ (reqFormat != mReqFormat)) {
+ mReqWidth = reqWidth;
+ mReqHeight = reqHeight;
+ mReqFormat = reqFormat;
+
+ lcblk->reallocateAllExcept(index);
+ }
}
+ // here we have to reallocate a new buffer because the buffer could be
+ // used as the front buffer, or by a client in our process
+ // (eg: status bar), and we can't release the handle under its feet.
const uint32_t effectiveUsage = getEffectiveUsage(usage);
- if (buffer!=0 && buffer->getStrongCount() == 1) {
- err = buffer->reallocate(w, h, mFormat, effectiveUsage);
- } else {
- // here we have to reallocate a new buffer because we could have a
- // client in our process with a reference to it (eg: status bar),
- // and we can't release the handle under its feet.
- buffer.clear();
- buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage);
- err = buffer->initCheck();
- }
+ buffer = new GraphicBuffer(w, h, f, effectiveUsage);
+ err = buffer->initCheck();
if (err || buffer->handle == 0) {
LOGE_IF(err || buffer->handle == 0,
@@ -377,15 +347,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
if (err == NO_ERROR && buffer->handle != 0) {
Mutex::Autolock _l(mLock);
- if (mWidth && mHeight) {
- // and we have new buffer
- mBuffers[index] = buffer;
- // texture is now dirty...
- mTextures[index].dirty = true;
- } else {
- // oops we got killed while we were allocating the buffer
- buffer.clear();
- }
+ mBufferManager.attachBuffer(index, buffer);
}
return buffer;
}
@@ -411,15 +373,8 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const
} else {
// it's allowed to modify the usage flags here, but generally
// the requested flags should be honored.
- if (mNoEGLImageForSwBuffers) {
- if (usage & GraphicBuffer::USAGE_HW_MASK) {
- // request EGLImage for h/w buffers only
- usage |= GraphicBuffer::USAGE_HW_TEXTURE;
- }
- } else {
- // request EGLImage for all buffers
- usage |= GraphicBuffer::USAGE_HW_TEXTURE;
- }
+ // request EGLImage for all buffers
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
}
return usage;
}
@@ -429,42 +384,50 @@ uint32_t Layer::doTransaction(uint32_t flags)
const Layer::State& front(drawingState());
const Layer::State& temp(currentState());
- if ((front.requested_w != temp.requested_w) ||
- (front.requested_h != temp.requested_h)) {
+ const bool sizeChanged = (front.requested_w != temp.requested_w) ||
+ (front.requested_h != temp.requested_h);
+
+ if (sizeChanged) {
// the size changed, we need to ask our client to request a new buffer
LOGD_IF(DEBUG_RESIZE,
- "resize (layer=%p), requested (%dx%d), "
- "drawing (%d,%d), (%dx%d), (%dx%d)",
- this,
- int(temp.requested_w), int(temp.requested_h),
- int(front.requested_w), int(front.requested_h),
- int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()),
- int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight()));
-
- // we're being resized and there is a freeze display request,
- // acquire a freeze lock, so that the screen stays put
- // until we've redrawn at the new size; this is to avoid
- // glitches upon orientation changes.
- if (mFlinger->hasFreezeRequest()) {
- // if the surface is hidden, don't try to acquire the
- // freeze lock, since hidden surfaces may never redraw
- if (!(front.flags & ISurfaceComposer::eLayerHidden)) {
- mFreezeLock = mFlinger->getFreezeLock();
+ "resize (layer=%p), requested (%dx%d), drawing (%d,%d)",
+ this,
+ int(temp.requested_w), int(temp.requested_h),
+ int(front.requested_w), int(front.requested_h));
+
+ if (!isFixedSize()) {
+ // we're being resized and there is a freeze display request,
+ // acquire a freeze lock, so that the screen stays put
+ // until we've redrawn at the new size; this is to avoid
+ // glitches upon orientation changes.
+ if (mFlinger->hasFreezeRequest()) {
+ // if the surface is hidden, don't try to acquire the
+ // freeze lock, since hidden surfaces may never redraw
+ if (!(front.flags & ISurfaceComposer::eLayerHidden)) {
+ mFreezeLock = mFlinger->getFreezeLock();
+ }
}
- }
-
- // this will make sure LayerBase::doTransaction doesn't update
- // the drawing state's size
- Layer::State& editDraw(mDrawingState);
- editDraw.requested_w = temp.requested_w;
- editDraw.requested_h = temp.requested_h;
-
- // record the new size, form this point on, when the client request a
- // buffer, it'll get the new size.
- setDrawingSize(temp.requested_w, temp.requested_h);
- // all buffers need reallocation
- lcblk->reallocate();
+ // this will make sure LayerBase::doTransaction doesn't update
+ // the drawing state's size
+ Layer::State& editDraw(mDrawingState);
+ editDraw.requested_w = temp.requested_w;
+ editDraw.requested_h = temp.requested_h;
+
+ // record the new size, form this point on, when the client request
+ // a buffer, it'll get the new size.
+ setBufferSize(temp.requested_w, temp.requested_h);
+
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ // all buffers need reallocation
+ lcblk->reallocateAll();
+ }
+ } else {
+ // record the new size
+ setBufferSize(temp.requested_w, temp.requested_h);
+ }
}
if (temp.sequence != front.sequence) {
@@ -478,39 +441,55 @@ uint32_t Layer::doTransaction(uint32_t flags)
return LayerBase::doTransaction(flags);
}
-void Layer::setDrawingSize(uint32_t w, uint32_t h) {
+void Layer::setBufferSize(uint32_t w, uint32_t h) {
Mutex::Autolock _l(mLock);
mWidth = w;
mHeight = h;
}
+bool Layer::isFixedSize() const {
+ Mutex::Autolock _l(mLock);
+ return mFixedSize;
+}
+
// ----------------------------------------------------------------------------
// pageflip handling...
// ----------------------------------------------------------------------------
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
+ // client died
+ recomputeVisibleRegions = true;
+ return;
+ }
+
ssize_t buf = lcblk->retireAndLock();
- if (buf < NO_ERROR) {
- //LOGW("nothing to retire (%s)", strerror(-buf));
- // NOTE: here the buffer is locked because we will used
+ if (buf == NOT_ENOUGH_DATA) {
+ // NOTE: This is not an error, it simply means there is nothing to
+ // retire. The buffer is locked because we will use it
// for composition later in the loop
return;
}
- // ouch, this really should never happen
- if (uint32_t(buf)>=NUM_BUFFERS) {
- LOGE("retireAndLock() buffer index (%d) out of range", buf);
+ if (buf < NO_ERROR) {
+ LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
mPostedDirtyRegion.clear();
return;
}
// we retired a buffer, which becomes the new front buffer
- mFrontBufferIndex = buf;
+ if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) {
+ LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
+ mPostedDirtyRegion.clear();
+ return;
+ }
- // get the dirty region
sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
if (newFrontBuffer != NULL) {
+ // get the dirty region
// compute the posted region
const Region dirty(lcblk->getDirtyRegion(buf));
mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
@@ -546,6 +525,13 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions)
// we now have the correct size, unfreeze the screen
mFreezeLock.clear();
}
+
+ // get the crop region
+ setBufferCrop( lcblk->getCrop(buf) );
+
+ // get the transformation
+ setBufferTransform( lcblk->getTransform(buf) );
+
} else {
// this should not happen unless we ran out of memory while
// allocating the buffer. we're hoping that things will get back
@@ -559,9 +545,15 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions)
mFlinger->signalEvent();
}
- if (!mPostedDirtyRegion.isEmpty()) {
- reloadTexture( mPostedDirtyRegion );
- }
+ /* a buffer was posted, so we need to call reloadTexture(), which
+ * will update our internal data structures (eg: EGLImageKHR or
+ * texture names). we need to do this even if mPostedDirtyRegion is
+ * empty -- it's orthogonal to the fact that a new buffer was posted,
+ * for instance, a degenerate case could be that the user did an empty
+ * update but repainted the buffer with appropriate content (after a
+ * resize for instance).
+ */
+ reloadTexture( mPostedDirtyRegion );
}
void Layer::unlockPageFlip(
@@ -585,24 +577,265 @@ void Layer::unlockPageFlip(
}
if (visibleRegionScreen.isEmpty()) {
// an invisible layer should not hold a freeze-lock
- // (because it may never be updated and thereore never release it)
+ // (because it may never be updated and therefore never release it)
mFreezeLock.clear();
}
}
void Layer::finishPageFlip()
{
- status_t err = lcblk->unlock( mFrontBufferIndex );
- LOGE_IF(err!=NO_ERROR,
- "layer %p, buffer=%d wasn't locked!",
- this, mFrontBufferIndex);
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ int buf = mBufferManager.getActiveBufferIndex();
+ if (buf >= 0) {
+ status_t err = lcblk->unlock( buf );
+ LOGE_IF(err!=NO_ERROR,
+ "layer %p, buffer=%d wasn't locked!",
+ this, buf);
+ }
+ }
+}
+
+
+void Layer::dump(String8& result, char* buffer, size_t SIZE) const
+{
+ LayerBaseClient::dump(result, buffer, SIZE);
+
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ uint32_t totalTime = 0;
+ if (lcblk) {
+ SharedBufferStack::Statistics stats = lcblk->getStats();
+ totalTime= stats.totalTime;
+ result.append( lcblk->dump(" ") );
+ }
+
+ sp<const GraphicBuffer> buf0(getBuffer(0));
+ sp<const GraphicBuffer> buf1(getBuffer(1));
+ uint32_t w0=0, h0=0, s0=0;
+ uint32_t w1=0, h1=0, s1=0;
+ if (buf0 != 0) {
+ w0 = buf0->getWidth();
+ h0 = buf0->getHeight();
+ s0 = buf0->getStride();
+ }
+ if (buf1 != 0) {
+ w1 = buf1->getWidth();
+ h1 = buf1->getHeight();
+ s1 = buf1->getStride();
+ }
+ snprintf(buffer, SIZE,
+ " "
+ "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u],"
+ " freezeLock=%p, dq-q-time=%u us\n",
+ mFormat, w0, h0, s0, w1, h1, s1,
+ getFreezeLock().get(), totalTime);
+
+ result.append(buffer);
+}
+
+// ---------------------------------------------------------------------------
+
+Layer::ClientRef::ClientRef()
+ : mControlBlock(0), mToken(-1) {
+}
+
+Layer::ClientRef::~ClientRef() {
+}
+
+int32_t Layer::ClientRef::getToken() const {
+ Mutex::Autolock _l(mLock);
+ return mToken;
+}
+
+sp<UserClient> Layer::ClientRef::getClient() const {
+ Mutex::Autolock _l(mLock);
+ return mUserClient.promote();
+}
+
+status_t Layer::ClientRef::setToken(const sp<UserClient>& uc,
+ const sp<SharedBufferServer>& sharedClient, int32_t token) {
+ Mutex::Autolock _l(mLock);
+
+ { // scope for strong mUserClient reference
+ sp<UserClient> userClient(mUserClient.promote());
+ if (mUserClient != 0 && mControlBlock != 0) {
+ mControlBlock->setStatus(NO_INIT);
+ }
+ }
+
+ mUserClient = uc;
+ mToken = token;
+ mControlBlock = sharedClient;
+ return NO_ERROR;
+}
+
+sp<UserClient> Layer::ClientRef::getUserClientUnsafe() const {
+ return mUserClient.promote();
+}
+
+// this class gives us access to SharedBufferServer safely
+// it makes sure the UserClient (and its associated shared memory)
+// won't go away while we're accessing it.
+Layer::ClientRef::Access::Access(const ClientRef& ref)
+ : mControlBlock(0)
+{
+ Mutex::Autolock _l(ref.mLock);
+ mUserClientStrongRef = ref.mUserClient.promote();
+ if (mUserClientStrongRef != 0)
+ mControlBlock = ref.mControlBlock;
+}
+
+Layer::ClientRef::Access::~Access()
+{
+}
+
+// ---------------------------------------------------------------------------
+
+Layer::BufferManager::BufferManager(TextureManager& tm)
+ : mNumBuffers(NUM_BUFFERS), mTextureManager(tm),
+ mActiveBuffer(-1), mFailover(false)
+{
+}
+
+Layer::BufferManager::~BufferManager()
+{
+}
+
+status_t Layer::BufferManager::resize(size_t size)
+{
+ Mutex::Autolock _l(mLock);
+ mNumBuffers = size;
+ return NO_ERROR;
+}
+
+// only for debugging
+sp<GraphicBuffer> Layer::BufferManager::getBuffer(size_t index) const {
+ return mBufferData[index].buffer;
+}
+
+status_t Layer::BufferManager::setActiveBufferIndex(size_t index) {
+ mActiveBuffer = index;
+ return NO_ERROR;
+}
+
+size_t Layer::BufferManager::getActiveBufferIndex() const {
+ return mActiveBuffer;
+}
+
+Texture Layer::BufferManager::getActiveTexture() const {
+ Texture res;
+ if (mFailover || mActiveBuffer<0) {
+ res = mFailoverTexture;
+ } else {
+ static_cast<Image&>(res) = mBufferData[mActiveBuffer].texture;
+ }
+ return res;
+}
+
+sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const {
+ sp<GraphicBuffer> result;
+ const ssize_t activeBuffer = mActiveBuffer;
+ if (activeBuffer >= 0) {
+ BufferData const * const buffers = mBufferData;
+ Mutex::Autolock _l(mLock);
+ result = buffers[activeBuffer].buffer;
+ }
+ return result;
+}
+
+sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index)
+{
+ BufferData* const buffers = mBufferData;
+ sp<GraphicBuffer> buffer;
+ Mutex::Autolock _l(mLock);
+ buffer = buffers[index].buffer;
+ buffers[index].buffer = 0;
+ return buffer;
+}
+
+status_t Layer::BufferManager::attachBuffer(size_t index,
+ const sp<GraphicBuffer>& buffer)
+{
+ BufferData* const buffers = mBufferData;
+ Mutex::Autolock _l(mLock);
+ buffers[index].buffer = buffer;
+ buffers[index].texture.dirty = true;
+ return NO_ERROR;
+}
+
+status_t Layer::BufferManager::destroy(EGLDisplay dpy)
+{
+ BufferData* const buffers = mBufferData;
+ size_t num;
+ { // scope for the lock
+ Mutex::Autolock _l(mLock);
+ num = mNumBuffers;
+ for (size_t i=0 ; i<num ; i++) {
+ buffers[i].buffer = 0;
+ }
+ }
+ for (size_t i=0 ; i<num ; i++) {
+ destroyTexture(&buffers[i].texture, dpy);
+ }
+ destroyTexture(&mFailoverTexture, dpy);
+ return NO_ERROR;
+}
+
+status_t Layer::BufferManager::initEglImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& buffer)
+{
+ status_t err = NO_INIT;
+ ssize_t index = mActiveBuffer;
+ if (index >= 0) {
+ if (!mFailover) {
+ Image& texture(mBufferData[index].texture);
+ err = mTextureManager.initEglImage(&texture, dpy, buffer);
+ // if EGLImage fails, we switch to regular texture mode, and we
+ // free all resources associated with using EGLImages.
+ if (err == NO_ERROR) {
+ mFailover = false;
+ destroyTexture(&mFailoverTexture, dpy);
+ } else {
+ mFailover = true;
+ const size_t num = mNumBuffers;
+ for (size_t i=0 ; i<num ; i++) {
+ destroyTexture(&mBufferData[i].texture, dpy);
+ }
+ }
+ } else {
+ // we failed once, don't try again
+ err = BAD_VALUE;
+ }
+ }
+ return err;
+}
+
+status_t Layer::BufferManager::loadTexture(
+ const Region& dirty, const GGLSurface& t)
+{
+ return mTextureManager.loadTexture(&mFailoverTexture, dirty, t);
+}
+
+status_t Layer::BufferManager::destroyTexture(Image* tex, EGLDisplay dpy)
+{
+ if (tex->name != -1U) {
+ glDeleteTextures(1, &tex->name);
+ tex->name = -1U;
+ }
+ if (tex->image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(dpy, tex->image);
+ tex->image = EGL_NO_IMAGE_KHR;
+ }
+ return NO_ERROR;
}
// ---------------------------------------------------------------------------
Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<Layer>& owner)
- : Surface(flinger, id, owner->getIdentity(), owner)
+ const sp<Layer>& owner)
+ : Surface(flinger, owner->getIdentity(), owner)
{
}
@@ -610,20 +843,37 @@ Layer::SurfaceLayer::~SurfaceLayer()
{
}
-sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage)
+sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage)
{
sp<GraphicBuffer> buffer;
sp<Layer> owner(getOwner());
if (owner != 0) {
- LOGE_IF(uint32_t(index)>=NUM_BUFFERS,
- "getBuffer() index (%d) out of range", index);
- if (uint32_t(index) < NUM_BUFFERS) {
- buffer = owner->requestBuffer(index, usage);
- }
+ /*
+ * requestBuffer() cannot be called from the main thread
+ * as it could cause a dead-lock, since it may have to wait
+ * on conditions updated my the main thread.
+ */
+ buffer = owner->requestBuffer(index, w, h, format, usage);
}
return buffer;
}
+status_t Layer::SurfaceLayer::setBufferCount(int bufferCount)
+{
+ status_t err = DEAD_OBJECT;
+ sp<Layer> owner(getOwner());
+ if (owner != 0) {
+ /*
+ * setBufferCount() cannot be called from the main thread
+ * as it could cause a dead-lock, since it may have to wait
+ * on conditions updated my the main thread.
+ */
+ err = owner->setBufferCount(bufferCount);
+ }
+ return err;
+}
+
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 743afb4..e1d283b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -31,36 +31,43 @@
#include "LayerBase.h"
#include "Transform.h"
+#include "TextureManager.h"
namespace android {
// ---------------------------------------------------------------------------
-class Client;
class FreezeLock;
+class Client;
+class GLExtensions;
+class UserClient;
// ---------------------------------------------------------------------------
-const size_t NUM_BUFFERS = 2;
-
class Layer : public LayerBaseClient
{
-public:
- static const uint32_t typeInfo;
- static const char* const typeID;
- virtual char const* getTypeID() const { return typeID; }
- virtual uint32_t getTypeInfo() const { return typeInfo; }
-
- Layer(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+public:
+ Layer(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client);
- virtual ~Layer();
+ virtual ~Layer();
+ virtual const char* getTypeId() const { return "Layer"; }
+
+ // the this layer's size and format
status_t setBuffers(uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags=0);
- void setDrawingSize(uint32_t w, uint32_t h);
+ // associate a UserClient to this Layer
+ status_t setToken(const sp<UserClient>& uc, SharedClient* sc, int32_t idx);
+ int32_t getToken() const;
+ sp<UserClient> getClient() const;
+
+ // Set this Layer's buffers size
+ void setBufferSize(uint32_t w, uint32_t h);
+ bool isFixedSize() const;
+ // LayerBase interface
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions);
@@ -68,63 +75,161 @@ public:
virtual void finishPageFlip();
virtual bool needsBlending() const { return mNeedsBlending; }
virtual bool needsDithering() const { return mNeedsDithering; }
+ virtual bool needsFiltering() const;
virtual bool isSecure() const { return mSecure; }
virtual sp<Surface> createSurface() const;
virtual status_t ditch();
-
- // only for debugging
- inline sp<GraphicBuffer> getBuffer(int i) { return mBuffers[i]; }
- // only for debugging
- inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; }
+ virtual void onRemoved();
+
// only for debugging
- inline PixelFormat pixelFormat() const { return mFormat; }
+ inline sp<GraphicBuffer> getBuffer(int i) const {
+ return mBufferManager.getBuffer(i); }
// only for debugging
- inline int getFrontBufferIndex() const { return mFrontBufferIndex; }
+ inline const sp<FreezeLock>& getFreezeLock() const {
+ return mFreezeLock; }
+
+protected:
+ virtual void dump(String8& result, char* scratch, size_t size) const;
private:
- inline sp<GraphicBuffer> getFrontBufferLocked() {
- return mBuffers[mFrontBufferIndex];
- }
-
void reloadTexture(const Region& dirty);
-
uint32_t getEffectiveUsage(uint32_t usage) const;
+ sp<GraphicBuffer> requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ status_t setBufferCount(int bufferCount);
- sp<GraphicBuffer> requestBuffer(int index, int usage);
- void destroy();
+ // -----------------------------------------------------------------------
class SurfaceLayer : public LayerBaseClient::Surface {
public:
- SurfaceLayer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<Layer>& owner);
+ SurfaceLayer(const sp<SurfaceFlinger>& flinger, const sp<Layer>& owner);
~SurfaceLayer();
private:
- virtual sp<GraphicBuffer> requestBuffer(int index, int usage);
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ virtual status_t setBufferCount(int bufferCount);
sp<Layer> getOwner() const {
return static_cast<Layer*>(Surface::getOwner().get());
}
};
friend class SurfaceLayer;
-
- sp<Surface> mSurface;
-
- bool mSecure;
- bool mNoEGLImageForSwBuffers;
- int32_t mFrontBufferIndex;
- bool mNeedsBlending;
- bool mNeedsDithering;
- Region mPostedDirtyRegion;
- sp<FreezeLock> mFreezeLock;
- PixelFormat mFormat;
-
- // protected by mLock
- sp<GraphicBuffer> mBuffers[NUM_BUFFERS];
- Texture mTextures[NUM_BUFFERS];
- sp<GraphicBuffer> mHybridBuffer;
- uint32_t mWidth;
- uint32_t mHeight;
-
- mutable Mutex mLock;
+
+ // -----------------------------------------------------------------------
+
+ class ClientRef {
+ ClientRef(const ClientRef& rhs);
+ ClientRef& operator = (const ClientRef& rhs);
+ mutable Mutex mLock;
+ // binder thread, page-flip thread
+ sp<SharedBufferServer> mControlBlock;
+ wp<UserClient> mUserClient;
+ int32_t mToken;
+ public:
+ ClientRef();
+ ~ClientRef();
+ int32_t getToken() const;
+ sp<UserClient> getClient() const;
+ status_t setToken(const sp<UserClient>& uc,
+ const sp<SharedBufferServer>& sharedClient, int32_t token);
+ sp<UserClient> getUserClientUnsafe() const;
+ class Access {
+ Access(const Access& rhs);
+ Access& operator = (const Access& rhs);
+ sp<UserClient> mUserClientStrongRef;
+ sp<SharedBufferServer> mControlBlock;
+ public:
+ Access(const ClientRef& ref);
+ ~Access();
+ inline SharedBufferServer* get() const { return mControlBlock.get(); }
+ };
+ friend class Access;
+ };
+
+ // -----------------------------------------------------------------------
+
+ class BufferManager {
+ static const size_t NUM_BUFFERS = 2;
+ struct BufferData {
+ sp<GraphicBuffer> buffer;
+ Image texture;
+ };
+ // this lock protect mBufferData[].buffer but since there
+ // is very little contention, we have only one like for
+ // the whole array, we also use it to protect mNumBuffers.
+ mutable Mutex mLock;
+ BufferData mBufferData[SharedBufferStack::NUM_BUFFER_MAX];
+ size_t mNumBuffers;
+ Texture mFailoverTexture;
+ TextureManager& mTextureManager;
+ ssize_t mActiveBuffer;
+ bool mFailover;
+ static status_t destroyTexture(Image* tex, EGLDisplay dpy);
+
+ public:
+ static size_t getDefaultBufferCount() { return NUM_BUFFERS; }
+ BufferManager(TextureManager& tm);
+ ~BufferManager();
+
+ // detach/attach buffer from/to given index
+ sp<GraphicBuffer> detachBuffer(size_t index);
+ status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer);
+ // resize the number of active buffers
+ status_t resize(size_t size);
+
+ // ----------------------------------------------
+ // must be called from GL thread
+
+ // set/get active buffer index
+ status_t setActiveBufferIndex(size_t index);
+ size_t getActiveBufferIndex() const;
+ // return the active buffer
+ sp<GraphicBuffer> getActiveBuffer() const;
+ // return the active texture (or fail-over)
+ Texture getActiveTexture() const;
+ // frees resources associated with all buffers
+ status_t destroy(EGLDisplay dpy);
+ // load bitmap data into the active buffer
+ status_t loadTexture(const Region& dirty, const GGLSurface& t);
+ // make active buffer an EGLImage if needed
+ status_t initEglImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& buffer);
+
+ // ----------------------------------------------
+ // only for debugging
+ sp<GraphicBuffer> getBuffer(size_t index) const;
+ };
+
+ // -----------------------------------------------------------------------
+
+ // thread-safe
+ ClientRef mUserClientRef;
+
+ // constants
+ sp<Surface> mSurface;
+ PixelFormat mFormat;
+ const GLExtensions& mGLExtensions;
+ bool mNeedsBlending;
+ bool mNeedsDithering;
+
+ // page-flip thread (currently main thread)
+ bool mSecure;
+ Region mPostedDirtyRegion;
+
+ // page-flip thread and transaction thread (currently main thread)
+ sp<FreezeLock> mFreezeLock;
+
+ // see threading usage in declaration
+ TextureManager mTextureManager;
+ BufferManager mBufferManager;
+
+ // binder thread, transaction thread
+ mutable Mutex mLock;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mReqWidth;
+ uint32_t mReqHeight;
+ uint32_t mReqFormat;
+ bool mFixedSize;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index a8b735e..6fc5010 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -32,33 +32,30 @@
#include "LayerBase.h"
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
+#include "TextureManager.h"
namespace android {
// ---------------------------------------------------------------------------
-const uint32_t LayerBase::typeInfo = 1;
-const char* const LayerBase::typeID = "LayerBase";
-
-const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2;
-const char* const LayerBaseClient::typeID = "LayerBaseClient";
-
-// ---------------------------------------------------------------------------
+int32_t LayerBase::sSequence = 1;
LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
: dpy(display), contentDirty(false),
+ sequence(uint32_t(android_atomic_inc(&sSequence))),
mFlinger(flinger),
- mTransformed(false),
- mUseLinearFiltering(false),
+ mNeedsFiltering(false),
mOrientation(0),
mLeft(0), mTop(0),
mTransactionFlags(0),
- mPremultipliedAlpha(true), mDebug(false),
+ mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
mInvalidate(0)
{
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
mFlags = hw.getFlags();
+ mBufferCrop.makeInvalid();
+ mBufferTransform = 0;
}
LayerBase::~LayerBase()
@@ -159,7 +156,6 @@ bool LayerBase::setAlpha(uint8_t alpha) {
return true;
}
bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) {
- // TODO: check the matrix has changed
mCurrentState.sequence++;
mCurrentState.transform.set(
matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy);
@@ -167,7 +163,6 @@ bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) {
return true;
}
bool LayerBase::setTransparentRegionHint(const Region& transparent) {
- // TODO: check the region has changed
mCurrentState.sequence++;
mCurrentState.transparentRegion = transparent;
requestTransaction();
@@ -221,13 +216,12 @@ uint32_t LayerBase::doTransaction(uint32_t flags)
flags |= eVisibleRegion;
this->contentDirty = true;
- const bool linearFiltering = mUseLinearFiltering;
- mUseLinearFiltering = false;
+ mNeedsFiltering = false;
if (!(mFlags & DisplayHardware::SLOW_CONFIG)) {
// we may use linear filtering, if the matrix scales us
const uint8_t type = temp.transform.getType();
if (!temp.transform.preserveRects() || (type >= Transform::SCALE)) {
- mUseLinearFiltering = true;
+ mNeedsFiltering = true;
}
}
}
@@ -267,7 +261,6 @@ void LayerBase::validateVisibility(const Transform& planeTransform)
// cache a few things...
mOrientation = tr.getOrientation();
mTransformedBounds = tr.makeBounds(w, h);
- mTransformed = transformed;
mLeft = tr.tx();
mTop = tr.ty();
}
@@ -316,65 +309,31 @@ void LayerBase::drawRegion(const Region& reg) const
}
}
-void LayerBase::draw(const Region& inClip) const
+void LayerBase::draw(const Region& clip) const
{
- // invalidate the region we'll update
- Region clip(inClip); // copy-on-write, so no-op most of the time
-
- // Remove the transparent area from the clipping region
- const State& s = drawingState();
- if (LIKELY(!s.transparentRegion.isEmpty())) {
- clip.subtract(transparentRegionScreen);
- if (clip.isEmpty()) {
- // usually this won't happen because this should be taken care of
- // by SurfaceFlinger::computeVisibleRegions()
- return;
- }
- }
-
// reset GL state
glEnable(GL_SCISSOR_TEST);
onDraw(clip);
-
- /*
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_DITHER);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glColor4x(0, 0x8000, 0, 0x10000);
- drawRegion(transparentRegionScreen);
- glDisable(GL_BLEND);
- */
}
-GLuint LayerBase::createTexture() const
-{
- GLuint textureName = -1;
- glGenTextures(1, &textureName);
- glBindTexture(GL_TEXTURE_2D, textureName);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- return textureName;
-}
-
-void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red,
- GLclampx green, GLclampx blue,
- GLclampx alpha) const
+void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red,
+ GLclampf green, GLclampf blue,
+ GLclampf alpha) const
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const uint32_t fbHeight = hw.getHeight();
- glColor4x(red,green,blue,alpha);
- glDisable(GL_TEXTURE_2D);
+ glColor4f(red,green,blue,alpha);
+
+ TextureManager::deactivateTextures();
+
glDisable(GL_BLEND);
glDisable(GL_DITHER);
Region::const_iterator it = clip.begin();
Region::const_iterator const end = clip.end();
glEnable(GL_SCISSOR_TEST);
- glVertexPointer(2, GL_FIXED, 0, mVertices);
+ glVertexPointer(2, GL_FLOAT, 0, mVertices);
while (it != end) {
const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
@@ -388,6 +347,14 @@ void LayerBase::clearWithOpenGL(const Region& clip) const
clearWithOpenGL(clip,0,0,0,0);
}
+template <typename T>
+static inline
+void swap(T& a, T& b) {
+ T t(a);
+ a = b;
+ b = t;
+}
+
void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
@@ -395,39 +362,25 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
const State& s(drawingState());
// bind our texture
- validateTexture(texture.name);
+ TextureManager::activateTexture(texture, needsFiltering());
uint32_t width = texture.width;
uint32_t height = texture.height;
-
- glEnable(GL_TEXTURE_2D);
+ GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
if (UNLIKELY(s.alpha < 0xFF)) {
- // We have an alpha-modulation. We need to modulate all
- // texture components by alpha because we're always using
- // premultiplied alpha.
-
- // If the texture doesn't have an alpha channel we can
- // use REPLACE and switch to non premultiplied alpha
- // blending (SRCA/ONE_MINUS_SRCA).
-
- GLenum env, src;
- if (needsBlending()) {
- env = GL_MODULATE;
- src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
+ const GLfloat alpha = s.alpha * (1.0f/255.0f);
+ if (mPremultipliedAlpha) {
+ glColor4f(alpha, alpha, alpha, alpha);
} else {
- env = GL_REPLACE;
- src = GL_SRC_ALPHA;
+ glColor4f(1, 1, 1, alpha);
}
- const GGLfixed alpha = (s.alpha << 16)/255;
- glColor4x(alpha, alpha, alpha, alpha);
glEnable(GL_BLEND);
glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
} else {
+ glColor4f(1, 1, 1, 1);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
if (needsBlending()) {
- GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
glEnable(GL_BLEND);
glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
} else {
@@ -435,44 +388,85 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
}
}
- Region::const_iterator it = clip.begin();
- Region::const_iterator const end = clip.end();
+ /*
+ * compute texture coordinates
+ * here, we handle NPOT, cropping and buffer transformations
+ */
+
+ GLfloat cl, ct, cr, cb;
+ if (!mBufferCrop.isEmpty()) {
+ // source is cropped
+ const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width;
+ const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height;
+ cl = mBufferCrop.left * us;
+ ct = mBufferCrop.top * vs;
+ cr = mBufferCrop.right * us;
+ cb = mBufferCrop.bottom * vs;
+ } else {
+ cl = 0;
+ ct = 0;
+ cr = (texture.NPOTAdjust ? texture.wScale : 1.0f);
+ cb = (texture.NPOTAdjust ? texture.hScale : 1.0f);
+ }
- //StopWatch watch("GL transformed");
- const GLfixed texCoords[4][2] = {
- { 0, 0 },
- { 0, 0x10000 },
- { 0x10000, 0x10000 },
- { 0x10000, 0 }
+ struct TexCoords {
+ GLfloat u;
+ GLfloat v;
};
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
+ enum {
+ // name of the corners in the texture map
+ LB = 0, // left-bottom
+ LT = 1, // left-top
+ RT = 2, // right-top
+ RB = 3 // right-bottom
+ };
+
+ // vertices in screen space
+ int vLT = LB;
+ int vLB = LT;
+ int vRB = RT;
+ int vRT = RB;
// the texture's source is rotated
- switch (texture.transform) {
- case HAL_TRANSFORM_ROT_90:
- glTranslatef(0, 1, 0);
- glRotatef(-90, 0, 0, 1);
- break;
- case HAL_TRANSFORM_ROT_180:
- glTranslatef(1, 1, 0);
- glRotatef(-180, 0, 0, 1);
- break;
- case HAL_TRANSFORM_ROT_270:
- glTranslatef(1, 0, 0);
- glRotatef(-270, 0, 0, 1);
- break;
+ uint32_t transform = mBufferTransform;
+ if (transform & HAL_TRANSFORM_ROT_90) {
+ vLT = RB;
+ vLB = LB;
+ vRB = LT;
+ vRT = RT;
+ }
+ if (transform & HAL_TRANSFORM_FLIP_V) {
+ swap(vLT, vLB);
+ swap(vRB, vRT);
}
+ if (transform & HAL_TRANSFORM_FLIP_H) {
+ swap(vLT, vRB);
+ swap(vLB, vRT);
+ }
+
+ TexCoords texCoords[4];
+ texCoords[vLT].u = cl;
+ texCoords[vLT].v = ct;
+ texCoords[vLB].u = cl;
+ texCoords[vLB].v = cb;
+ texCoords[vRB].u = cr;
+ texCoords[vRB].v = cb;
+ texCoords[vRT].u = cr;
+ texCoords[vRT].v = ct;
- if (texture.NPOTAdjust) {
- glScalef(texture.wScale, texture.hScale, 1.0f);
+ if (needsDithering()) {
+ glEnable(GL_DITHER);
+ } else {
+ glDisable(GL_DITHER);
}
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glVertexPointer(2, GL_FIXED, 0, mVertices);
- glTexCoordPointer(2, GL_FIXED, 0, texCoords);
+ glVertexPointer(2, GL_FLOAT, 0, mVertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
while (it != end) {
const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
@@ -482,246 +476,50 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
-void LayerBase::validateTexture(GLint textureName) const
-{
- glBindTexture(GL_TEXTURE_2D, textureName);
- // TODO: reload the texture if needed
- // this is currently done in loadTexture() below
- if (mUseLinearFiltering) {
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- } else {
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- }
-
- if (needsDithering()) {
- glEnable(GL_DITHER);
- } else {
- glDisable(GL_DITHER);
- }
-}
-
-bool LayerBase::isSupportedYuvFormat(int format) const
-{
- switch (format) {
- case HAL_PIXEL_FORMAT_YCbCr_422_SP:
- case HAL_PIXEL_FORMAT_YCbCr_420_SP:
- case HAL_PIXEL_FORMAT_YCbCr_422_P:
- case HAL_PIXEL_FORMAT_YCbCr_420_P:
- case HAL_PIXEL_FORMAT_YCbCr_422_I:
- case HAL_PIXEL_FORMAT_YCbCr_420_I:
- case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- return true;
+void LayerBase::setBufferCrop(const Rect& crop) {
+ if (!crop.isEmpty()) {
+ mBufferCrop = crop;
}
- return false;
}
-void LayerBase::loadTexture(Texture* texture,
- const Region& dirty, const GGLSurface& t) const
-{
- if (texture->name == -1U) {
- // uh?
- return;
- }
-
- glBindTexture(GL_TEXTURE_2D, texture->name);
-
- /*
- * In OpenGL ES we can't specify a stride with glTexImage2D (however,
- * GL_UNPACK_ALIGNMENT is a limited form of stride).
- * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
- * need to do something reasonable (here creating a bigger texture).
- *
- * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
- *
- * This situation doesn't happen often, but some h/w have a limitation
- * for their framebuffer (eg: must be multiple of 8 pixels), and
- * we need to take that into account when using these buffers as
- * textures.
- *
- * This should never be a problem with POT textures
- */
-
- int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
- unpack = 1 << ((unpack > 3) ? 3 : unpack);
- glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
-
- /*
- * round to POT if needed
- */
- if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
- texture->NPOTAdjust = true;
- }
-
- if (texture->NPOTAdjust) {
- // find the smallest power-of-two that will accommodate our surface
- texture->potWidth = 1 << (31 - clz(t.width));
- texture->potHeight = 1 << (31 - clz(t.height));
- if (texture->potWidth < t.width) texture->potWidth <<= 1;
- if (texture->potHeight < t.height) texture->potHeight <<= 1;
- texture->wScale = float(t.width) / texture->potWidth;
- texture->hScale = float(t.height) / texture->potHeight;
- } else {
- texture->potWidth = t.width;
- texture->potHeight = t.height;
- }
-
- Rect bounds(dirty.bounds());
- GLvoid* data = 0;
- if (texture->width != t.width || texture->height != t.height) {
- texture->width = t.width;
- texture->height = t.height;
-
- // texture size changed, we need to create a new one
- bounds.set(Rect(t.width, t.height));
- if (t.width == texture->potWidth &&
- t.height == texture->potHeight) {
- // we can do it one pass
- data = t.data;
- }
-
- if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGB, texture->potWidth, texture->potHeight, 0,
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
- } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGBA, texture->potWidth, texture->potHeight, 0,
- GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
- } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
- t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGBA, texture->potWidth, texture->potHeight, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, data);
- } else if (isSupportedYuvFormat(t.format)) {
- // just show the Y plane of YUV buffers
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
- GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
- } else {
- // oops, we don't handle this format!
- LOGE("layer %p, texture=%d, using format %d, which is not "
- "supported by the GL", this, texture->name, t.format);
- }
- }
- if (!data) {
- if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
- t.data + bounds.top*t.stride*2);
- } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
- t.data + bounds.top*t.stride*2);
- } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
- t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGBA, GL_UNSIGNED_BYTE,
- t.data + bounds.top*t.stride*4);
- } else if (isSupportedYuvFormat(t.format)) {
- // just show the Y plane of YUV buffers
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_LUMINANCE, GL_UNSIGNED_BYTE,
- t.data + bounds.top*t.stride);
- }
- }
+void LayerBase::setBufferTransform(uint32_t transform) {
+ mBufferTransform = transform;
}
-status_t LayerBase::initializeEglImage(
- const sp<GraphicBuffer>& buffer, Texture* texture)
+void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
{
- status_t err = NO_ERROR;
-
- // we need to recreate the texture
- EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
-
- // free the previous image
- if (texture->image != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(dpy, texture->image);
- texture->image = EGL_NO_IMAGE_KHR;
- }
-
- // construct an EGL_NATIVE_BUFFER_ANDROID
- android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
-
- // create the new EGLImageKHR
- const EGLint attrs[] = {
- EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
- EGL_NONE, EGL_NONE
- };
- texture->image = eglCreateImageKHR(
- dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- (EGLClientBuffer)clientBuf, attrs);
-
- if (texture->image != EGL_NO_IMAGE_KHR) {
- glBindTexture(GL_TEXTURE_2D, texture->name);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
- (GLeglImageOES)texture->image);
- GLint error = glGetError();
- if (UNLIKELY(error != GL_NO_ERROR)) {
- LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) "
- "failed err=0x%04x",
- this, texture->image, error);
- err = INVALID_OPERATION;
- } else {
- // Everything went okay!
- texture->NPOTAdjust = false;
- texture->dirty = false;
- texture->width = clientBuf->width;
- texture->height = clientBuf->height;
- }
- } else {
- LOGE("layer=%p, eglCreateImageKHR() failed. err=0x%4x",
- this, eglGetError());
- err = INVALID_OPERATION;
- }
- return err;
+ const Layer::State& s(drawingState());
+ snprintf(buffer, SIZE,
+ "+ %s %p\n"
+ " "
+ "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), "
+ "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, "
+ "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
+ getTypeId(), this, s.z, tx(), ty(), s.w, s.h,
+ needsBlending(), needsDithering(), contentDirty,
+ s.alpha, s.flags,
+ s.transform[0][0], s.transform[0][1],
+ s.transform[1][0], s.transform[1][1]);
+ result.append(buffer);
}
-
// ---------------------------------------------------------------------------
-int32_t LayerBaseClient::sIdentity = 0;
+int32_t LayerBaseClient::sIdentity = 1;
LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i),
+ const sp<Client>& client)
+ : LayerBase(flinger, display), mClientRef(client),
mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
{
- lcblk = new SharedBufferServer(
- client->ctrlblk, i, NUM_BUFFERS,
- mIdentity);
-}
-
-void LayerBaseClient::onFirstRef()
-{
- sp<Client> client(this->client.promote());
- if (client != 0) {
- client->bindLayer(this, mIndex);
- }
}
LayerBaseClient::~LayerBaseClient()
{
- sp<Client> client(this->client.promote());
- if (client != 0) {
- client->free(mIndex);
- }
- delete lcblk;
-}
-
-int32_t LayerBaseClient::serverIndex() const
-{
- sp<Client> client(this->client.promote());
- if (client != 0) {
- return (client->cid<<16)|mIndex;
+ sp<Client> c(mClientRef.promote());
+ if (c != 0) {
+ c->detachLayer(this);
}
- return 0xFFFF0000 | mIndex;
}
sp<LayerBaseClient::Surface> LayerBaseClient::getSurface()
@@ -738,25 +536,31 @@ sp<LayerBaseClient::Surface> LayerBaseClient::getSurface()
sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const
{
- return new Surface(mFlinger, clientIndex(), mIdentity,
+ return new Surface(mFlinger, mIdentity,
const_cast<LayerBaseClient *>(this));
}
-// called with SurfaceFlinger::mStateLock as soon as the layer is entered
-// in the purgatory list
-void LayerBaseClient::onRemoved()
+void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const
{
- // wake up the condition
- lcblk->setStatus(NO_INIT);
+ LayerBase::dump(result, buffer, SIZE);
+
+ sp<Client> client(mClientRef.promote());
+ snprintf(buffer, SIZE,
+ " name=%s\n"
+ " client=%p, identity=%u\n",
+ getName().string(),
+ client.get(), getIdentity());
+
+ result.append(buffer);
}
// ---------------------------------------------------------------------------
LayerBaseClient::Surface::Surface(
const sp<SurfaceFlinger>& flinger,
- SurfaceID id, int identity,
+ int identity,
const sp<LayerBaseClient>& owner)
- : mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner)
+ : mFlinger(flinger), mIdentity(identity), mOwner(owner)
{
}
@@ -799,11 +603,17 @@ status_t LayerBaseClient::Surface::onTransact(
return BnSurface::onTransact(code, data, reply, flags);
}
-sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int index, int usage)
+sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage)
{
return NULL;
}
+status_t LayerBaseClient::Surface::setBufferCount(int bufferCount)
+{
+ return INVALID_OPERATION;
+}
+
status_t LayerBaseClient::Surface::registerBuffers(
const ISurface::BufferHeap& buffers)
{
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 62ec839..8cba287 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -29,7 +29,7 @@
#include <ui/Region.h>
#include <ui/Overlay.h>
-#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
#include <private/surfaceflinger/SharedBufferStack.h>
#include <private/surfaceflinger/LayerState.h>
@@ -45,46 +45,25 @@ class DisplayHardware;
class Client;
class GraphicBuffer;
class GraphicPlane;
+class LayerBaseClient;
class SurfaceFlinger;
+class Texture;
// ---------------------------------------------------------------------------
class LayerBase : public RefBase
{
- // poor man's dynamic_cast below
- template<typename T>
- struct getTypeInfoOfAnyType {
- static uint32_t get() { return T::typeInfo; }
- };
-
- template<typename T>
- struct getTypeInfoOfAnyType<T*> {
- static uint32_t get() { return getTypeInfoOfAnyType<T>::get(); }
- };
+ static int32_t sSequence;
public:
- static const uint32_t typeInfo;
- static const char* const typeID;
- virtual char const* getTypeID() const { return typeID; }
- virtual uint32_t getTypeInfo() const { return typeInfo; }
-
- template<typename T>
- static T dynamicCast(LayerBase* base) {
- uint32_t mostDerivedInfo = base->getTypeInfo();
- uint32_t castToInfo = getTypeInfoOfAnyType<T>::get();
- if ((mostDerivedInfo & castToInfo) == castToInfo)
- return static_cast<T>(base);
- return 0;
- }
+ LayerBase(SurfaceFlinger* flinger, DisplayID display);
-
- LayerBase(SurfaceFlinger* flinger, DisplayID display);
-
DisplayID dpy;
mutable bool contentDirty;
Region visibleRegionScreen;
Region transparentRegionScreen;
Region coveredRegionScreen;
+ int32_t sequence;
struct State {
uint32_t w;
@@ -125,6 +104,10 @@ public:
void invalidate();
+ virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; }
+
+ virtual const char* getTypeId() const { return "LayerBase"; }
+
/**
* draw - performs some global clipping optimizations
* and calls onDraw().
@@ -199,9 +182,9 @@ public:
virtual bool needsDithering() const { return false; }
/**
- * transformed -- true is this surface needs a to be transformed
+ * needsLinearFiltering - true if this surface needs filtering
*/
- virtual bool transformed() const { return mTransformed; }
+ virtual bool needsFiltering() const { return mNeedsFiltering; }
/**
* isSecure - true if this surface is secure, that is if it prevents
@@ -217,7 +200,10 @@ public:
* current list */
virtual void onRemoved() { };
-
+ /** always call base class first */
+ virtual void dump(String8& result, char* scratch, size_t size) const;
+
+
enum { // flags for doTransaction()
eVisibleRegion = 0x00000002,
};
@@ -227,12 +213,6 @@ public:
inline const State& currentState() const { return mCurrentState; }
inline State& currentState() { return mCurrentState; }
- static int compareCurrentStateZ(
- sp<LayerBase> const * layerA,
- sp<LayerBase> const * layerB) {
- return layerA[0]->currentState().z - layerB[0]->currentState().z;
- }
-
int32_t getOrientation() const { return mOrientation; }
int tx() const { return mLeft; }
int ty() const { return mTop; }
@@ -241,44 +221,26 @@ protected:
const GraphicPlane& graphicPlane(int dpy) const;
GraphicPlane& graphicPlane(int dpy);
- GLuint createTexture() const;
-
- struct Texture {
- Texture() : name(-1U), width(0), height(0),
- image(EGL_NO_IMAGE_KHR), transform(0),
- NPOTAdjust(false), dirty(true) { }
- GLuint name;
- GLuint width;
- GLuint height;
- GLuint potWidth;
- GLuint potHeight;
- GLfloat wScale;
- GLfloat hScale;
- EGLImageKHR image;
- uint32_t transform;
- bool NPOTAdjust;
- bool dirty;
- };
-
- void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g,
- GLclampx b, GLclampx alpha) const;
+ void clearWithOpenGL(const Region& clip, GLclampf r, GLclampf g,
+ GLclampf b, GLclampf alpha) const;
void clearWithOpenGL(const Region& clip) const;
void drawWithOpenGL(const Region& clip, const Texture& texture) const;
- void loadTexture(Texture* texture,
- const Region& dirty, const GGLSurface& t) const;
- status_t initializeEglImage(
- const sp<GraphicBuffer>& buffer, Texture* texture);
-
- bool isSupportedYuvFormat(int format) const;
+ // these must be called from the post/drawing thread
+ void setBufferCrop(const Rect& crop);
+ void setBufferTransform(uint32_t transform);
+
sp<SurfaceFlinger> mFlinger;
uint32_t mFlags;
+ // post/drawing thread
+ Rect mBufferCrop;
+ uint32_t mBufferTransform;
+
// cached during validateVisibility()
- bool mTransformed;
- bool mUseLinearFiltering;
+ bool mNeedsFiltering;
int32_t mOrientation;
- GLfixed mVertices[4][2];
+ GLfloat mVertices[4][2];
Rect mTransformedBounds;
int mLeft;
int mTop;
@@ -303,7 +265,6 @@ protected:
private:
LayerBase(const LayerBase& rhs);
- void validateTexture(GLint textureName) const;
};
@@ -313,42 +274,25 @@ class LayerBaseClient : public LayerBase
{
public:
class Surface;
- static const uint32_t typeInfo;
- static const char* const typeID;
- virtual char const* getTypeID() const { return typeID; }
- virtual uint32_t getTypeInfo() const { return typeInfo; }
-
- // lcblk is (almost) only accessed from the main SF thread, in the places
- // where it's not, a reference to Client must be held
- SharedBufferServer* lcblk;
- LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client);
virtual ~LayerBaseClient();
- virtual void onFirstRef();
-
- const wp<Client> client;
- inline uint32_t getIdentity() const { return mIdentity; }
- inline int32_t clientIndex() const { return mIndex; }
- int32_t serverIndex() const;
-
-
sp<Surface> getSurface();
virtual sp<Surface> createSurface() const;
-
- virtual void onRemoved();
+ virtual sp<LayerBaseClient> getLayerBaseClient() const {
+ return const_cast<LayerBaseClient*>(this); }
+ virtual const char* getTypeId() const { return "LayerBaseClient"; }
+ uint32_t getIdentity() const { return mIdentity; }
- class Surface : public BnSurface
- {
+ class Surface : public BnSurface {
public:
- int32_t getToken() const { return mToken; }
int32_t getIdentity() const { return mIdentity; }
protected:
- Surface(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, int identity,
+ Surface(const sp<SurfaceFlinger>& flinger, int identity,
const sp<LayerBaseClient>& owner);
virtual ~Surface();
virtual status_t onTransact(uint32_t code, const Parcel& data,
@@ -356,7 +300,10 @@ public:
sp<LayerBaseClient> getOwner() const;
private:
- virtual sp<GraphicBuffer> requestBuffer(int index, int usage);
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ virtual status_t setBufferCount(int bufferCount);
+
virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
@@ -366,20 +313,22 @@ public:
protected:
friend class LayerBaseClient;
sp<SurfaceFlinger> mFlinger;
- int32_t mToken;
int32_t mIdentity;
wp<LayerBaseClient> mOwner;
};
friend class Surface;
+protected:
+ virtual void dump(String8& result, char* scratch, size_t size) const;
+
private:
- int32_t mIndex;
- mutable Mutex mLock;
- mutable wp<Surface> mClientSurface;
+ mutable Mutex mLock;
+ mutable wp<Surface> mClientSurface;
+ const wp<Client> mClientRef;
// only read
- const uint32_t mIdentity;
- static int32_t sIdentity;
+ const uint32_t mIdentity;
+ static int32_t sIdentity;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
index 5fd7904..2ee21b9 100644
--- a/services/surfaceflinger/LayerBlur.cpp
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -33,14 +33,9 @@
namespace android {
// ---------------------------------------------------------------------------
-const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8;
-const char* const LayerBlur::typeID = "LayerBlur";
-
-// ---------------------------------------------------------------------------
-
LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBaseClient(flinger, display, client, i), mCacheDirty(true),
+ const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client), mCacheDirty(true),
mRefreshCache(true), mCacheAge(0), mTextureName(-1U),
mWidthScale(1.0f), mHeightScale(1.0f),
mBlurFormat(GGL_PIXEL_FORMAT_RGB_565)
@@ -100,7 +95,9 @@ void LayerBlur::unlockPageFlip(const Transform& planeTransform, Region& outDirty
mCacheDirty = false;
} else {
if (!mAutoRefreshPending) {
- mFlinger->signalDelayedEvent(ms2ns(500));
+ mFlinger->postMessageAsync(
+ new MessageBase(MessageQueue::INVALIDATE),
+ ms2ns(500));
mAutoRefreshPending = true;
}
}
@@ -149,6 +146,11 @@ void LayerBlur::onDraw(const Region& clip) const
Region::const_iterator it = clip.begin();
Region::const_iterator const end = clip.end();
if (it != end) {
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+#endif
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mTextureName);
@@ -181,7 +183,7 @@ void LayerBlur::onDraw(const Region& clip) const
bl.data = (GGLubyte*)pixels;
blurFilter(&bl, 8, 2);
- if (mFlags & (DisplayHardware::NPOT_EXTENSION)) {
+ if (GLExtensions::getInstance().haveNpot()) {
glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0,
mReadFormat, mReadType, pixels);
mWidthScale = 1.0f / w;
@@ -206,8 +208,8 @@ void LayerBlur::onDraw(const Region& clip) const
const State& s = drawingState();
if (UNLIKELY(s.alpha < 0xFF)) {
- const GGLfixed alpha = (s.alpha << 16)/255;
- glColor4x(0, 0, 0, alpha);
+ const GLfloat alpha = s.alpha * (1.0f/255.0f);
+ glColor4f(0, 0, 0, alpha);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@@ -225,38 +227,22 @@ void LayerBlur::onDraw(const Region& clip) const
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- if (UNLIKELY(transformed()
- || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) {
- // This is a very rare scenario.
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
- glScalef(mWidthScale, mHeightScale, 1);
- glTranslatef(-x, mYOffset - y, 0);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glVertexPointer(2, GL_FIXED, 0, mVertices);
- glTexCoordPointer(2, GL_FIXED, 0, mVertices);
- while (it != end) {
- const Rect& r = *it++;
- const GLint sy = fbHeight - (r.top + r.height());
- glScissor(r.left, sy, r.width(), r.height());
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- }
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- } else {
- // NOTE: this is marginally faster with the software gl, because
- // glReadPixels() reads the fb bottom-to-top, however we'll
- // skip all the jaccobian computations.
- Rect r;
- GLint crop[4] = { 0, 0, w, h };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- y = fbHeight - (y + h);
- while (it != end) {
- const Rect& r = *it++;
- const GLint sy = fbHeight - (r.top + r.height());
- glScissor(r.left, sy, r.width(), r.height());
- glDrawTexiOES(x, y, 0, w, h);
- }
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glScalef(mWidthScale, mHeightScale, 1);
+ glTranslatef(-x, mYOffset - y, 0);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, mVertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, mVertices);
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
}
}
diff --git a/services/surfaceflinger/LayerBlur.h b/services/surfaceflinger/LayerBlur.h
index 5b63dec..4c9ec64 100644
--- a/services/surfaceflinger/LayerBlur.h
+++ b/services/surfaceflinger/LayerBlur.h
@@ -31,18 +31,14 @@ namespace android {
class LayerBlur : public LayerBaseClient
{
public:
- static const uint32_t typeInfo;
- static const char* const typeID;
- virtual char const* getTypeID() const { return typeID; }
- virtual uint32_t getTypeInfo() const { return typeInfo; }
-
LayerBlur(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ const sp<Client>& client);
virtual ~LayerBlur();
virtual void onDraw(const Region& clip) const;
virtual bool needsBlending() const { return true; }
virtual bool isSecure() const { return false; }
+ virtual const char* getTypeId() const { return "LayerBlur"; }
virtual uint32_t doTransaction(uint32_t flags);
virtual void setVisibleRegion(const Region& visibleRegion);
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 0869283..fdf9abc 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -39,15 +39,13 @@ namespace android {
// ---------------------------------------------------------------------------
-const uint32_t LayerBuffer::typeInfo = LayerBaseClient::typeInfo | 0x20;
-const char* const LayerBuffer::typeID = "LayerBuffer";
gralloc_module_t const* LayerBuffer::sGrallocModule = 0;
// ---------------------------------------------------------------------------
LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBaseClient(flinger, display, client, i),
+ const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client),
mNeedsBlending(false), mBlitEngine(0)
{
}
@@ -62,8 +60,7 @@ LayerBuffer::~LayerBuffer()
void LayerBuffer::onFirstRef()
{
LayerBaseClient::onFirstRef();
- mSurface = new SurfaceLayerBuffer(mFlinger, clientIndex(),
- const_cast<LayerBuffer *>(this));
+ mSurface = new SurfaceLayerBuffer(mFlinger, this);
hw_module_t const* module = (hw_module_t const*)sGrallocModule;
if (!module) {
@@ -120,7 +117,7 @@ uint32_t LayerBuffer::doTransaction(uint32_t flags)
source->onTransaction(flags);
uint32_t res = LayerBase::doTransaction(flags);
// we always want filtering for these surfaces
- mUseLinearFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG);
+ mNeedsFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG);
return res;
}
@@ -145,14 +142,6 @@ void LayerBuffer::onDraw(const Region& clip) const
}
}
-bool LayerBuffer::transformed() const
-{
- sp<Source> source(getSource());
- if (LIKELY(source != 0))
- return source->transformed();
- return false;
-}
-
void LayerBuffer::serverDestroy()
{
sp<Source> source(clearSource());
@@ -214,9 +203,9 @@ sp<LayerBuffer::Source> LayerBuffer::clearSource() {
// LayerBuffer::SurfaceLayerBuffer
// ============================================================================
-LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<LayerBuffer>& owner)
- : LayerBaseClient::Surface(flinger, id, owner->getIdentity(), owner)
+LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(
+ const sp<SurfaceFlinger>& flinger, const sp<LayerBuffer>& owner)
+ : LayerBaseClient::Surface(flinger, owner->getIdentity(), owner)
{
}
@@ -321,16 +310,12 @@ void LayerBuffer::Source::postBuffer(ssize_t offset) {
}
void LayerBuffer::Source::unregisterBuffers() {
}
-bool LayerBuffer::Source::transformed() const {
- return mLayer.mTransformed;
-}
// ---------------------------------------------------------------------------
LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
const ISurface::BufferHeap& buffers)
- : Source(layer), mStatus(NO_ERROR), mBufferSize(0),
- mUseEGLImageDirectly(true)
+ : Source(layer), mStatus(NO_ERROR), mBufferSize(0)
{
if (buffers.heap == NULL) {
// this is allowed, but in this case, it is illegal to receive
@@ -388,11 +373,11 @@ LayerBuffer::BufferSource::~BufferSource()
if (mTexture.name != -1U) {
// GL textures can only be destroyed from the GL thread
- mLayer.mFlinger->mEventQueue.postMessage(
- new MessageDestroyTexture(mLayer.mFlinger.get(), mTexture.name) );
+ getFlinger()->mEventQueue.postMessage(
+ new MessageDestroyTexture(getFlinger(), mTexture.name) );
}
if (mTexture.image != EGL_NO_IMAGE_KHR) {
- EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+ EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay());
eglDestroyImageKHR(dpy, mTexture.image);
}
}
@@ -444,11 +429,6 @@ void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer)
mBuffer = buffer;
}
-bool LayerBuffer::BufferSource::transformed() const
-{
- return mBufferHeap.transform ? true : Source::transformed();
-}
-
void LayerBuffer::BufferSource::onDraw(const Region& clip) const
{
sp<Buffer> ourBuffer(getBuffer());
@@ -462,35 +442,10 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
NativeBuffer src(ourBuffer->getBuffer());
const Rect transformedBounds(mLayer.getTransformedBounds());
- if (UNLIKELY(mTexture.name == -1LU)) {
- mTexture.name = mLayer.createTexture();
- }
-
#if defined(EGL_ANDROID_image_native_buffer)
- if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
+ if (GLExtensions::getInstance().haveDirectTexture()) {
err = INVALID_OPERATION;
if (ourBuffer->supportsCopybit()) {
-
- // there are constraints on buffers used by the GPU and these may not
- // be honored here. We need to change the API so the buffers
- // are allocated with gralloc. For now disable this code-path
-#if 0
- // First, try to use the buffer as an EGLImage directly
- if (mUseEGLImageDirectly) {
- // NOTE: Assume the buffer is allocated with the proper USAGE flags
-
- sp<GraphicBuffer> buffer = new GraphicBuffer(
- src.img.w, src.img.h, src.img.format,
- GraphicBuffer::USAGE_HW_TEXTURE,
- src.img.w, src.img.handle, false);
-
- err = mLayer.initializeEglImage(buffer, &mTexture);
- if (err != NO_ERROR) {
- mUseEGLImageDirectly = false;
- }
- }
-#endif
-
copybit_device_t* copybit = mLayer.mBlitEngine;
if (copybit && err != NO_ERROR) {
// create our EGLImageKHR the first time
@@ -527,10 +482,10 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
t.format = src.img.format;
t.data = (GGLubyte*)src.img.base;
const Region dirty(Rect(t.width, t.height));
- mLayer.loadTexture(&mTexture, dirty, t);
+ mTextureManager.loadTexture(&mTexture, dirty, t);
}
- mTexture.transform = mBufferHeap.transform;
+ mLayer.setBufferTransform(mBufferHeap.transform);
mLayer.drawWithOpenGL(clip, mTexture);
}
@@ -569,7 +524,7 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const
// figure out if we need linear filtering
if (buffers.w * h == buffers.h * w) {
// same pixel area, don't use filtering
- mLayer.mUseLinearFiltering = false;
+ mLayer.mNeedsFiltering = false;
}
// Allocate a temporary buffer and create the corresponding EGLImageKHR
@@ -593,7 +548,8 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const
dst.crop.r = w;
dst.crop.b = h;
- err = mLayer.initializeEglImage(buffer, &mTexture);
+ EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay());
+ err = mTextureManager.initEglImage(&mTexture, dpy, buffer);
}
return err;
@@ -602,14 +558,13 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const
void LayerBuffer::BufferSource::clearTempBufferImage() const
{
// delete the image
- EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+ EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay());
eglDestroyImageKHR(dpy, mTexture.image);
// and the associated texture (recreate a name)
glDeleteTextures(1, &mTexture.name);
Texture defaultTexture;
mTexture = defaultTexture;
- mTexture.name = mLayer.createTexture();
}
// ---------------------------------------------------------------------------
@@ -620,7 +575,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
: Source(layer), mVisibilityChanged(false),
mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation)
{
- overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine();
+ overlay_control_device_t* overlay_dev = getFlinger()->getOverlayEngine();
if (overlay_dev == NULL) {
// overlays not supported
return;
@@ -651,7 +606,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
*overlayRef = new OverlayRef(mOverlayHandle, channel,
mWidth, mHeight, mFormat, mWidthStride, mHeightStride);
- mLayer.mFlinger->signalEvent();
+ getFlinger()->signalEvent();
}
LayerBuffer::OverlaySource::~OverlaySource()
@@ -665,9 +620,9 @@ LayerBuffer::OverlaySource::~OverlaySource()
void LayerBuffer::OverlaySource::onDraw(const Region& clip) const
{
// this would be where the color-key would be set, should we need it.
- GLclampx red = 0;
- GLclampx green = 0;
- GLclampx blue = 0;
+ GLclampf red = 0;
+ GLclampf green = 0;
+ GLclampf blue = 0;
mLayer.clearWithOpenGL(clip, red, green, blue, 0);
}
diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h
index b176623..1c0bf83 100644
--- a/services/surfaceflinger/LayerBuffer.h
+++ b/services/surfaceflinger/LayerBuffer.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include "LayerBase.h"
+#include "TextureManager.h"
struct copybit_device_t;
@@ -45,31 +46,26 @@ class LayerBuffer : public LayerBaseClient
virtual void onVisibilityResolved(const Transform& planeTransform);
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
- virtual bool transformed() const;
virtual void destroy() { }
+ SurfaceFlinger* getFlinger() const { return mLayer.mFlinger.get(); }
protected:
LayerBuffer& mLayer;
};
public:
- static const uint32_t typeInfo;
- static const char* const typeID;
- virtual char const* getTypeID() const { return typeID; }
- virtual uint32_t getTypeInfo() const { return typeInfo; }
-
LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ const sp<Client>& client);
virtual ~LayerBuffer();
virtual void onFirstRef();
virtual bool needsBlending() const;
+ virtual const char* getTypeId() const { return "LayerBuffer"; }
virtual sp<LayerBaseClient::Surface> createSurface() const;
virtual status_t ditch();
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t flags);
virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
- virtual bool transformed() const;
status_t registerBuffers(const ISurface::BufferHeap& buffers);
void postBuffer(ssize_t offset);
@@ -133,7 +129,6 @@ private:
virtual void onDraw(const Region& clip) const;
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
- virtual bool transformed() const;
virtual void destroy() { }
private:
status_t initTempBuffer() const;
@@ -143,9 +138,9 @@ private:
status_t mStatus;
ISurface::BufferHeap mBufferHeap;
size_t mBufferSize;
- mutable LayerBase::Texture mTexture;
+ mutable Texture mTexture;
mutable NativeBuffer mTempBuffer;
- mutable bool mUseEGLImageDirectly;
+ mutable TextureManager mTextureManager;
};
class OverlaySource : public Source {
@@ -195,7 +190,7 @@ private:
{
public:
SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<LayerBuffer>& owner);
+ const sp<LayerBuffer>& owner);
virtual ~SurfaceLayerBuffer();
virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
index fd61e30..a1f339e 100644
--- a/services/surfaceflinger/LayerDim.cpp
+++ b/services/surfaceflinger/LayerDim.cpp
@@ -30,9 +30,6 @@
namespace android {
// ---------------------------------------------------------------------------
-const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10;
-const char* const LayerDim::typeID = "LayerDim";
-
bool LayerDim::sUseTexture;
GLuint LayerDim::sTexId;
EGLImageKHR LayerDim::sImage;
@@ -42,8 +39,8 @@ int32_t LayerDim::sHeight;
// ---------------------------------------------------------------------------
LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBaseClient(flinger, display, client, i)
+ const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client)
{
}
@@ -54,54 +51,6 @@ void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h)
sWidth = w;
sHeight = h;
sUseTexture = false;
-
-#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer)
-
-#warning "using a texture to implement LayerDim"
-
- /* On some h/w like msm7K, it is faster to use a texture because the
- * software renderer will defer to copybit, for this to work we need to
- * use an EGLImage texture so copybit can actually make use of it.
- * This burns a full-screen worth of graphic memory.
- */
-
- const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
- uint32_t flags = hw.getFlags();
-
- if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) {
- sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGB_565,
- GraphicBuffer::USAGE_SW_WRITE_OFTEN |
- GraphicBuffer::USAGE_HW_TEXTURE);
-
- android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
-
- glGenTextures(1, &sTexId);
- glBindTexture(GL_TEXTURE_2D, sTexId);
-
- EGLDisplay dpy = eglGetCurrentDisplay();
- sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
- EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0);
- if (sImage == EGL_NO_IMAGE_KHR) {
- LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
- return;
- }
-
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage);
- GLint error = glGetError();
- if (error != GL_NO_ERROR) {
- eglDestroyImageKHR(dpy, sImage);
- LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error);
- return;
- }
-
- // initialize the texture with zeros
- GGLSurface t;
- buffer->lock(&t, GRALLOC_USAGE_SW_WRITE_OFTEN);
- memset(t.data, 0, t.stride * t.height * 2);
- buffer->unlock();
- sUseTexture = true;
- }
-#endif
}
LayerDim::~LayerDim()
@@ -115,33 +64,19 @@ void LayerDim::onDraw(const Region& clip) const
Region::const_iterator const end = clip.end();
if (s.alpha>0 && (it != end)) {
const DisplayHardware& hw(graphicPlane(0).displayHardware());
- const GGLfixed alpha = (s.alpha << 16)/255;
+ const GLfloat alpha = s.alpha/255.0f;
const uint32_t fbHeight = hw.getHeight();
glDisable(GL_DITHER);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glColor4x(0, 0, 0, alpha);
-
-#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer)
- if (sUseTexture) {
- glBindTexture(GL_TEXTURE_2D, sTexId);
- glEnable(GL_TEXTURE_2D);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- const GLshort texCoords[4][2] = {
- { 0, 0 },
- { 0, 1 },
- { 1, 1 },
- { 1, 0 }
- };
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glTexCoordPointer(2, GL_SHORT, 0, texCoords);
- } else
-#endif
- {
- glDisable(GL_TEXTURE_2D);
+ glColor4f(0, 0, 0, alpha);
+
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
}
+#endif
+ glDisable(GL_TEXTURE_2D);
GLshort w = sWidth;
GLshort h = sHeight;
diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h
index d4672a1..f032314 100644
--- a/services/surfaceflinger/LayerDim.h
+++ b/services/surfaceflinger/LayerDim.h
@@ -37,18 +37,14 @@ class LayerDim : public LayerBaseClient
static int32_t sWidth;
static int32_t sHeight;
public:
- static const uint32_t typeInfo;
- static const char* const typeID;
- virtual char const* getTypeID() const { return typeID; }
- virtual uint32_t getTypeInfo() const { return typeInfo; }
-
LayerDim(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i);
+ const sp<Client>& client);
virtual ~LayerDim();
virtual void onDraw(const Region& clip) const;
virtual bool needsBlending() const { return true; }
virtual bool isSecure() const { return false; }
+ virtual const char* getTypeId() const { return "LayerDim"; }
static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h);
};
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index b43d801..d668e88 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -60,9 +60,9 @@ MessageQueue::~MessageQueue()
{
}
-MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
+sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout)
{
- MessageList::value_type result;
+ sp<MessageBase> result;
bool again;
do {
@@ -132,6 +132,7 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
if (again) {
// the message has been processed. release our reference to it
// without holding the lock.
+ result->notify();
result = 0;
}
@@ -141,7 +142,7 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
}
status_t MessageQueue::postMessage(
- const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
+ const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
{
return queueMessage(message, relTime, flags);
}
@@ -154,7 +155,7 @@ status_t MessageQueue::invalidate() {
}
status_t MessageQueue::queueMessage(
- const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
+ const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
{
Mutex::Autolock _l(mLock);
message->when = systemTime() + relTime;
@@ -167,13 +168,13 @@ status_t MessageQueue::queueMessage(
return NO_ERROR;
}
-void MessageQueue::dump(const MessageList::value_type& message)
+void MessageQueue::dump(const sp<MessageBase>& message)
{
Mutex::Autolock _l(mLock);
dumpLocked(message);
}
-void MessageQueue::dumpLocked(const MessageList::value_type& message)
+void MessageQueue::dumpLocked(const sp<MessageBase>& message)
{
LIST::const_iterator cur(mMessages.begin());
LIST::const_iterator end(mMessages.end());
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index dc8138d..890f809 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -25,6 +25,7 @@
#include <utils/Timers.h>
#include <utils/List.h>
+#include "Barrier.h"
namespace android {
@@ -37,7 +38,6 @@ class MessageList
List< sp<MessageBase> > mList;
typedef List< sp<MessageBase> > LIST;
public:
- typedef sp<MessageBase> value_type;
inline LIST::iterator begin() { return mList.begin(); }
inline LIST::const_iterator begin() const { return mList.begin(); }
inline LIST::iterator end() { return mList.end(); }
@@ -63,11 +63,19 @@ public:
// return true if message has a handler
virtual bool handler() { return false; }
+
+ // 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() { }
private:
+ mutable Barrier barrier;
friend class LightRefBase<MessageBase>;
};
@@ -82,42 +90,33 @@ class MessageQueue
typedef List< sp<MessageBase> > LIST;
public:
- // this is a work-around the multichar constant warning. A macro would
- // work too, but would pollute the namespace.
- template <int a, int b, int c, int d>
- struct WHAT {
- static const uint32_t Value =
- (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)|
- (uint32_t(c&0xff)<<8)|uint32_t(d&0xff);
- };
-
MessageQueue();
~MessageQueue();
// pre-defined messages
enum {
- INVALIDATE = WHAT<'_','p','d','t'>::Value
+ INVALIDATE = '_upd'
};
- MessageList::value_type waitMessage(nsecs_t timeout = -1);
+ sp<MessageBase> waitMessage(nsecs_t timeout = -1);
- status_t postMessage(const MessageList::value_type& message,
+ status_t postMessage(const sp<MessageBase>& message,
nsecs_t reltime=0, uint32_t flags = 0);
-
+
status_t invalidate();
- void dump(const MessageList::value_type& message);
+ void dump(const sp<MessageBase>& message);
private:
- status_t queueMessage(const MessageList::value_type& message,
+ status_t queueMessage(const sp<MessageBase>& message,
nsecs_t reltime, uint32_t flags);
- void dumpLocked(const MessageList::value_type& message);
+ void dumpLocked(const sp<MessageBase>& message);
Mutex mLock;
Condition mCondition;
MessageList mMessages;
bool mInvalidate;
- MessageList::value_type mInvalidateMessage;
+ sp<MessageBase> mInvalidateMessage;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0722fda..637ae48 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -44,6 +44,7 @@
#include <GLES/gl.h>
#include "clz.h"
+#include "GLExtensions.h"
#include "Layer.h"
#include "LayerBlur.h"
#include "LayerBuffer.h"
@@ -62,111 +63,8 @@
#define DISPLAY_COUNT 1
namespace android {
-
-// ---------------------------------------------------------------------------
-
-void SurfaceFlinger::instantiate() {
- defaultServiceManager()->addService(
- String16("SurfaceFlinger"), new SurfaceFlinger());
-}
-
-void SurfaceFlinger::shutdown() {
- // we should unregister here, but not really because
- // when (if) the service manager goes away, all the services
- // it has a reference to will leave too.
-}
-
// ---------------------------------------------------------------------------
-SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs)
- : lookup(rhs.lookup), layers(rhs.layers)
-{
-}
-
-ssize_t SurfaceFlinger::LayerVector::indexOf(
- const sp<LayerBase>& key, size_t guess) const
-{
- if (guess<size() && lookup.keyAt(guess) == key)
- return guess;
- const ssize_t i = lookup.indexOfKey(key);
- if (i>=0) {
- const size_t idx = lookup.valueAt(i);
- LOGE_IF(layers[idx]!=key,
- "LayerVector[%p]: layers[%d]=%p, key=%p",
- this, int(idx), layers[idx].get(), key.get());
- return idx;
- }
- return i;
-}
-
-ssize_t SurfaceFlinger::LayerVector::add(
- const sp<LayerBase>& layer,
- Vector< sp<LayerBase> >::compar_t cmp)
-{
- size_t count = layers.size();
- ssize_t l = 0;
- ssize_t h = count-1;
- ssize_t mid;
- sp<LayerBase> const* a = layers.array();
- while (l <= h) {
- mid = l + (h - l)/2;
- const int c = cmp(a+mid, &layer);
- if (c == 0) { l = mid; break; }
- else if (c<0) { l = mid+1; }
- else { h = mid-1; }
- }
- size_t order = l;
- while (order<count && !cmp(&layer, a+order)) {
- order++;
- }
- count = lookup.size();
- for (size_t i=0 ; i<count ; i++) {
- if (lookup.valueAt(i) >= order) {
- lookup.editValueAt(i)++;
- }
- }
- layers.insertAt(layer, order);
- lookup.add(layer, order);
- return order;
-}
-
-ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer)
-{
- const ssize_t keyIndex = lookup.indexOfKey(layer);
- if (keyIndex >= 0) {
- const size_t index = lookup.valueAt(keyIndex);
- LOGE_IF(layers[index]!=layer,
- "LayerVector[%p]: layers[%u]=%p, layer=%p",
- this, int(index), layers[index].get(), layer.get());
- layers.removeItemsAt(index);
- lookup.removeItemsAt(keyIndex);
- const size_t count = lookup.size();
- for (size_t i=0 ; i<count ; i++) {
- if (lookup.valueAt(i) >= size_t(index)) {
- lookup.editValueAt(i)--;
- }
- }
- return index;
- }
- return NAME_NOT_FOUND;
-}
-
-ssize_t SurfaceFlinger::LayerVector::reorder(
- const sp<LayerBase>& layer,
- Vector< sp<LayerBase> >::compar_t cmp)
-{
- // XXX: it's a little lame. but oh well...
- ssize_t err = remove(layer);
- if (err >=0)
- err = add(layer, cmp);
- return err;
-}
-
-// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(), Thread(false),
mTransactionFlags(0),
@@ -206,8 +104,8 @@ void SurfaceFlinger::init()
property_get("debug.sf.showbackground", value, "0");
mDebugBackground = atoi(value);
- LOGI_IF(mDebugRegion, "showupdates enabled");
- LOGI_IF(mDebugBackground, "showbackground enabled");
+ LOGI_IF(mDebugRegion, "showupdates enabled");
+ LOGI_IF(mDebugBackground, "showbackground enabled");
}
SurfaceFlinger::~SurfaceFlinger()
@@ -225,56 +123,29 @@ sp<IMemoryHeap> SurfaceFlinger::getCblk() const
return mServerHeap;
}
-sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection()
+sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
{
- Mutex::Autolock _l(mStateLock);
- uint32_t token = mTokens.acquire();
-
- sp<Client> client = new Client(token, this);
- if (client->ctrlblk == 0) {
- mTokens.release(token);
- return 0;
+ sp<ISurfaceComposerClient> bclient;
+ sp<Client> client(new Client(this));
+ status_t err = client->initCheck();
+ if (err == NO_ERROR) {
+ bclient = client;
}
- status_t err = mClientsMap.add(token, client);
- if (err < 0) {
- mTokens.release(token);
- return 0;
- }
- sp<BClient> bclient =
- new BClient(this, token, client->getControlBlockMemory());
return bclient;
}
-void SurfaceFlinger::destroyConnection(ClientID cid)
+sp<ISurfaceComposerClient> SurfaceFlinger::createClientConnection()
{
- Mutex::Autolock _l(mStateLock);
- sp<Client> client = mClientsMap.valueFor(cid);
- if (client != 0) {
- // free all the layers this client owns
- Vector< wp<LayerBaseClient> > layers(client->getLayers());
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; i++) {
- sp<LayerBaseClient> layer(layers[i].promote());
- if (layer != 0) {
- purgatorizeLayer_l(layer);
- }
- }
-
- // the resources associated with this client will be freed
- // during the next transaction, after these surfaces have been
- // properly removed from the screen
-
- // remove this client from our ClientID->Client mapping.
- mClientsMap.removeItem(cid);
-
- // and add it to the list of disconnected clients
- mDisconnectedClients.add(client);
-
- // request a transaction
- setTransactionFlags(eTransactionNeeded);
+ sp<ISurfaceComposerClient> bclient;
+ sp<UserClient> client(new UserClient(this));
+ status_t err = client->initCheck();
+ if (err == NO_ERROR) {
+ bclient = client;
}
+ return bclient;
}
+
const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const
{
LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy);
@@ -357,16 +228,8 @@ status_t SurfaceFlinger::readyToRun()
dcblk->ydpi = hw.getDpiY();
dcblk->fps = hw.getRefreshRate();
dcblk->density = hw.getDensity();
- asm volatile ("":::"memory");
// Initialize OpenGL|ES
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, 0);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glEnableClientState(GL_VERTEX_ARRAY);
@@ -427,7 +290,7 @@ void SurfaceFlinger::waitForEvent()
timeout = waitTime>0 ? waitTime : 0;
}
- MessageList::value_type msg = mEventQueue.waitMessage(timeout);
+ sp<MessageBase> msg = mEventQueue.waitMessage(timeout);
// see if we timed out
if (isFrozen()) {
@@ -462,9 +325,20 @@ void SurfaceFlinger::signal() const {
const_cast<SurfaceFlinger*>(this)->signalEvent();
}
-void SurfaceFlinger::signalDelayedEvent(nsecs_t delay)
+status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
+ nsecs_t reltime, uint32_t flags)
{
- mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay);
+ return mEventQueue.postMessage(msg, reltime, flags);
+}
+
+status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
+ nsecs_t reltime, uint32_t flags)
+{
+ status_t res = mEventQueue.postMessage(msg, reltime, flags);
+ if (res == NO_ERROR) {
+ msg->wait();
+ }
+ return res;
}
// ----------------------------------------------------------------------------
@@ -558,6 +432,10 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
{
Vector< sp<LayerBase> > ditchedLayers;
+ /*
+ * Perform and commit the transaction
+ */
+
{ // scope for the lock
Mutex::Autolock _l(mStateLock);
const nsecs_t now = systemTime();
@@ -565,9 +443,13 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
handleTransactionLocked(transactionFlags, ditchedLayers);
mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
+ // here the transaction has been committed
}
- // do this without lock held
+ /*
+ * Clean-up all layers that went away
+ * (do this without the lock held)
+ */
const size_t count = ditchedLayers.size();
for (size_t i=0 ; i<count ; i++) {
if (ditchedLayers[i] != 0) {
@@ -655,10 +537,6 @@ void SurfaceFlinger::handleTransactionLocked(
}
}
}
-
- // get rid of all resources we don't need anymore
- // (layers and clients)
- free_resources_l();
}
commitTransaction();
@@ -805,7 +683,8 @@ void SurfaceFlinger::commitTransaction()
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
- LayerVector& currentLayers = const_cast<LayerVector&>(mDrawingState.layersSortedByZ);
+ LayerVector& currentLayers = const_cast<LayerVector&>(
+ mDrawingState.layersSortedByZ);
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -813,6 +692,19 @@ void SurfaceFlinger::handlePageFlip()
if (visibleRegions) {
Region opaqueRegion;
computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
+
+ /*
+ * rebuild the visible layer list
+ */
+ mVisibleLayersSortedByZ.clear();
+ const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+ size_t count = currentLayers.size();
+ mVisibleLayersSortedByZ.setCapacity(count);
+ for (size_t i=0 ; i<count ; i++) {
+ if (!currentLayers[i]->visibleRegionScreen.isEmpty())
+ mVisibleLayersSortedByZ.add(currentLayers[i]);
+ }
+
mWormholeRegion = screenRegion.subtract(opaqueRegion);
mVisibleRegionsDirty = false;
}
@@ -827,7 +719,7 @@ bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)
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];
+ const sp<LayerBase>& layer(layers[i]);
layer->lockPageFlip(recomputeVisibleRegions);
}
return recomputeVisibleRegions;
@@ -840,7 +732,7 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
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];
+ const sp<LayerBase>& layer(layers[i]);
layer->unlockPageFlip(planeTransform, mDirtyRegion);
}
}
@@ -872,7 +764,7 @@ void SurfaceFlinger::handleRepaint()
// takes a rectangle, we must make sure to update that whole
// rectangle in that case
if (flags & DisplayHardware::SWAP_RECTANGLE) {
- // FIXME: we really should be able to pass a region to
+ // TODO: we really should be able to pass a region to
// SWAP_RECTANGLE so that we don't have to redraw all this.
mDirtyRegion.set(mInvalidRegion.bounds());
} else {
@@ -909,18 +801,13 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty)
// draw something...
drawWormhole();
}
- const SurfaceFlinger& flinger(*this);
- const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
- const size_t count = drawingLayers.size();
- sp<LayerBase> const* const layers = drawingLayers.array();
+ const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+ const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
- const sp<LayerBase>& layer = layers[i];
- const Region& visibleRegion(layer->visibleRegionScreen);
- if (!visibleRegion.isEmpty()) {
- const Region clip(dirty.intersect(visibleRegion));
- if (!clip.isEmpty()) {
- layer->draw(clip);
- }
+ const sp<LayerBase>& layer(layers[i]);
+ const Region clip(dirty.intersect(layer->visibleRegionScreen));
+ if (!clip.isEmpty()) {
+ layer->draw(clip);
}
}
}
@@ -938,17 +825,18 @@ void SurfaceFlinger::unlockClients()
void SurfaceFlinger::debugFlashRegions()
{
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- const uint32_t flags = hw.getFlags();
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const uint32_t flags = hw.getFlags();
+
+ if (!((flags & DisplayHardware::SWAP_RECTANGLE) ||
+ (flags & DisplayHardware::BUFFER_PRESERVED))) {
+ const Region repaint((flags & DisplayHardware::PARTIAL_UPDATES) ?
+ mDirtyRegion.bounds() : hw.bounds());
+ composeSurfaces(repaint);
+ }
+
+ TextureManager::deactivateTextures();
- if (!((flags & DisplayHardware::SWAP_RECTANGLE) ||
- (flags & DisplayHardware::BUFFER_PRESERVED))) {
- const Region repaint((flags & DisplayHardware::PARTIAL_UPDATES) ?
- mDirtyRegion.bounds() : hw.bounds());
- composeSurfaces(repaint);
- }
-
- glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
@@ -956,9 +844,9 @@ void SurfaceFlinger::debugFlashRegions()
static int toggle = 0;
toggle = 1 - toggle;
if (toggle) {
- glColor4x(0x10000, 0, 0x10000, 0x10000);
+ glColor4f(1, 0, 1, 1);
} else {
- glColor4x(0x10000, 0x10000, 0, 0x10000);
+ glColor4f(1, 1, 0, 1);
}
Region::const_iterator it = mDirtyRegion.begin();
@@ -974,7 +862,7 @@ void SurfaceFlinger::debugFlashRegions()
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
-
+
if (mInvalidRegion.isEmpty()) {
mDirtyRegion.dump("mDirtyRegion");
mInvalidRegion.dump("mInvalidRegion");
@@ -982,7 +870,7 @@ void SurfaceFlinger::debugFlashRegions()
hw.flip(mInvalidRegion);
if (mDebugRegion > 1)
- usleep(mDebugRegion * 1000);
+ usleep(mDebugRegion * 1000);
glEnable(GL_SCISSOR_TEST);
//mDirtyRegion.dump("mDirtyRegion");
@@ -1002,7 +890,7 @@ void SurfaceFlinger::drawWormhole() const
glDisable(GL_DITHER);
if (LIKELY(!mDebugBackground)) {
- glClearColorx(0,0,0,0);
+ glClearColor(0,0,0,0);
Region::const_iterator it = region.begin();
Region::const_iterator const end = region.end();
while (it != end) {
@@ -1018,6 +906,11 @@ void SurfaceFlinger::drawWormhole() const
glVertexPointer(2, GL_SHORT, 0, vertices);
glTexCoordPointer(2, GL_SHORT, 0, tcoords);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+#endif
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@@ -1061,6 +954,26 @@ status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer)
return NO_ERROR;
}
+status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
+{
+ ssize_t i = mCurrentState.layersSortedByZ.add(layer);
+ return (i < 0) ? status_t(i) : status_t(NO_ERROR);
+}
+
+ssize_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
+ const sp<LayerBaseClient>& lbc)
+{
+ Mutex::Autolock _l(mStateLock);
+
+ // attach this layer to the client
+ ssize_t name = client->attachLayer(lbc);
+
+ // add this layer to the current state list
+ addLayer_l(lbc);
+
+ return name;
+}
+
status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer)
{
Mutex::Autolock _l(mStateLock);
@@ -1070,36 +983,15 @@ status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer)
return err;
}
-status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
-{
- layer->forceVisibilityTransaction();
- setTransactionFlags(eTraversalNeeded);
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
+status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase)
{
- if (layer == 0)
- return BAD_VALUE;
- ssize_t i = mCurrentState.layersSortedByZ.add(
- layer, &LayerBase::compareCurrentStateZ);
- sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
+ sp<LayerBaseClient> lbc(layerBase->getLayerBaseClient());
if (lbc != 0) {
- mLayerMap.add(lbc->serverIndex(), lbc);
+ mLayerMap.removeItem( lbc->getSurface()->asBinder() );
}
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase)
-{
ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase);
if (index >= 0) {
mLayersRemoved = true;
- sp<LayerBaseClient> layer =
- LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get());
- if (layer != 0) {
- mLayerMap.removeItem(layer->serverIndex());
- }
return NO_ERROR;
}
return status_t(index);
@@ -1114,22 +1006,16 @@ status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase)
// it's possible that we don't find a layer, because it might
// have been destroyed already -- this is not technically an error
- // from the user because there is a race between BClient::destroySurface(),
- // ~BClient() and ~ISurface().
+ // from the user because there is a race between Client::destroySurface(),
+ // ~Client() and ~ISurface().
return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err;
}
-
-void SurfaceFlinger::free_resources_l()
+status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
{
- // free resources associated with disconnected clients
- Vector< sp<Client> >& disconnectedClients(mDisconnectedClients);
- const size_t count = disconnectedClients.size();
- for (size_t i=0 ; i<count ; i++) {
- sp<Client> client = disconnectedClients[i];
- mTokens.release(client->cid);
- }
- disconnectedClients.clear();
+ layer->forceVisibilityTransaction();
+ setTransactionFlags(eTraversalNeeded);
+ return NO_ERROR;
}
uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags)
@@ -1137,15 +1023,11 @@ uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags)
return android_atomic_and(~flags, &mTransactionFlags) & flags;
}
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay)
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags)
{
uint32_t old = android_atomic_or(flags, &mTransactionFlags);
if ((old & flags)==0) { // wake the server up
- if (delay > 0) {
- signalDelayedEvent(delay);
- } else {
- signalEvent();
- }
+ signalEvent();
}
return old;
}
@@ -1224,8 +1106,8 @@ int SurfaceFlinger::setOrientation(DisplayID dpy,
return orientation;
}
-sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
- const String8& name, ISurfaceFlingerClient::surface_data_t* params,
+sp<ISurface> SurfaceFlinger::createSurface(const sp<Client>& client, int pid,
+ const String8& name, ISurfaceComposerClient::surface_data_t* params,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
@@ -1238,57 +1120,52 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
return surfaceHandle;
}
- Mutex::Autolock _l(mStateLock);
- sp<Client> client = mClientsMap.valueFor(clientId);
- if (UNLIKELY(client == 0)) {
- LOGE("createSurface() failed, client not found (id=%d)", clientId);
- return surfaceHandle;
- }
-
//LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
- int32_t id = client->generateId(pid);
- if (uint32_t(id) >= NUM_LAYERS_MAX) {
- LOGE("createSurface() failed, generateId = %d", id);
- return surfaceHandle;
- }
-
+ sp<Layer> normalLayer;
switch (flags & eFXSurfaceMask) {
case eFXSurfaceNormal:
if (UNLIKELY(flags & ePushBuffers)) {
- layer = createPushBuffersSurfaceLocked(client, d, id,
- w, h, flags);
+ layer = createPushBuffersSurface(client, d, w, h, flags);
} else {
- layer = createNormalSurfaceLocked(client, d, id,
- w, h, flags, format);
+ normalLayer = createNormalSurface(client, d, w, h, flags, format);
+ layer = normalLayer;
}
break;
case eFXSurfaceBlur:
- layer = createBlurSurfaceLocked(client, d, id, w, h, flags);
+ layer = createBlurSurface(client, d, w, h, flags);
break;
case eFXSurfaceDim:
- layer = createDimSurfaceLocked(client, d, id, w, h, flags);
+ layer = createDimSurface(client, d, w, h, flags);
break;
}
if (layer != 0) {
+ layer->initStates(w, h, flags);
layer->setName(name);
- setTransactionFlags(eTransactionNeeded);
+ ssize_t token = addClientLayer(client, layer);
+
surfaceHandle = layer->getSurface();
if (surfaceHandle != 0) {
- params->token = surfaceHandle->getToken();
+ params->token = token;
params->identity = surfaceHandle->getIdentity();
params->width = w;
params->height = h;
params->format = format;
+ if (normalLayer != 0) {
+ Mutex::Autolock _l(mStateLock);
+ mLayerMap.add(surfaceHandle->asBinder(), normalLayer);
+ }
}
+
+ setTransactionFlags(eTransactionNeeded);
}
return surfaceHandle;
}
-sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
+sp<Layer> SurfaceFlinger::createNormalSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ uint32_t w, uint32_t h, uint32_t flags,
PixelFormat& format)
{
// initialize the surfaces
@@ -1298,53 +1175,56 @@ sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
format = PIXEL_FORMAT_RGBA_8888;
break;
case PIXEL_FORMAT_OPAQUE:
+#ifdef NO_RGBX_8888
format = PIXEL_FORMAT_RGB_565;
+#else
+ format = PIXEL_FORMAT_RGBX_8888;
+#endif
break;
}
- sp<Layer> layer = new Layer(this, display, client, id);
+#ifdef NO_RGBX_8888
+ if (format == PIXEL_FORMAT_RGBX_8888)
+ format = PIXEL_FORMAT_RGBA_8888;
+#endif
+
+ sp<Layer> layer = new Layer(this, display, client);
status_t err = layer->setBuffers(w, h, format, flags);
- if (LIKELY(err == NO_ERROR)) {
- layer->initStates(w, h, flags);
- addLayer_l(layer);
- } else {
+ if (LIKELY(err != NO_ERROR)) {
LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));
layer.clear();
}
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked(
+sp<LayerBlur> SurfaceFlinger::createBlurSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerBlur> layer = new LayerBlur(this, display, client, id);
+ sp<LayerBlur> layer = new LayerBlur(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked(
+sp<LayerDim> SurfaceFlinger::createDimSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerDim> layer = new LayerDim(this, display, client, id);
+ sp<LayerDim> layer = new LayerDim(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked(
+sp<LayerBuffer> SurfaceFlinger::createPushBuffersSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id);
+ sp<LayerBuffer> layer = new LayerBuffer(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
return layer;
}
-status_t SurfaceFlinger::removeSurface(SurfaceID index)
+status_t SurfaceFlinger::removeSurface(const sp<Client>& client, SurfaceID sid)
{
/*
* called by the window manager, when a surface should be marked for
@@ -1357,7 +1237,7 @@ status_t SurfaceFlinger::removeSurface(SurfaceID index)
status_t err = NAME_NOT_FOUND;
Mutex::Autolock _l(mStateLock);
- sp<LayerBaseClient> layer = getLayerUser_l(index);
+ sp<LayerBaseClient> layer = client->getLayerUser(sid);
if (layer != 0) {
err = purgatorizeLayer_l(layer);
if (err == NO_ERROR) {
@@ -1397,21 +1277,20 @@ status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer)
}
};
- mEventQueue.postMessage( new MessageDestroySurface(this, layer) );
+ postMessageAsync( new MessageDestroySurface(this, layer) );
return NO_ERROR;
}
status_t SurfaceFlinger::setClientState(
- ClientID cid,
+ const sp<Client>& client,
int32_t count,
const layer_state_t* states)
{
Mutex::Autolock _l(mStateLock);
uint32_t flags = 0;
- cid <<= 16;
for (int i=0 ; i<count ; i++) {
- const layer_state_t& s = states[i];
- sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid));
+ const layer_state_t& s(states[i]);
+ sp<LayerBaseClient> layer(client->getLayerUser(s.surface));
if (layer != 0) {
const uint32_t what = s.what;
if (what & ePositionChanged) {
@@ -1419,9 +1298,10 @@ status_t SurfaceFlinger::setClientState(
flags |= eTraversalNeeded;
}
if (what & eLayerChanged) {
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
if (layer->setLayer(s.z)) {
- mCurrentState.layersSortedByZ.reorder(
- layer, &Layer::compareCurrentStateZ);
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(layer);
// we need traversal (state changed)
// AND transaction (list changed)
flags |= eTransactionNeeded|eTraversalNeeded;
@@ -1457,12 +1337,6 @@ status_t SurfaceFlinger::setClientState(
return NO_ERROR;
}
-sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const
-{
- sp<LayerBaseClient> layer = mLayerMap.valueFor(s);
- return layer;
-}
-
void SurfaceFlinger::screenReleased(int dpy)
{
// this may be called by a signal handler, we can't do too much in here
@@ -1512,83 +1386,17 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
result.append(buffer);
}
- size_t s = mClientsMap.size();
- char name[64];
- for (size_t i=0 ; i<s ; i++) {
- sp<Client> client = mClientsMap.valueAt(i);
- sprintf(name, " Client (id=0x%08x)", client->cid);
- client->dump(name);
- }
const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
const size_t count = currentLayers.size();
for (size_t i=0 ; i<count ; i++) {
- /*** LayerBase ***/
- const sp<LayerBase>& layer = currentLayers[i];
- const Layer::State& s = layer->drawingState();
- snprintf(buffer, SIZE,
- "+ %s %p\n"
- " "
- "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), "
- "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, "
- "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
- layer->getTypeID(), layer.get(),
- s.z, layer->tx(), layer->ty(), s.w, s.h,
- layer->needsBlending(), layer->needsDithering(),
- layer->contentDirty,
- s.alpha, s.flags,
- s.transform[0][0], s.transform[0][1],
- s.transform[1][0], s.transform[1][1]);
- result.append(buffer);
- buffer[0] = 0;
- /*** LayerBaseClient ***/
- sp<LayerBaseClient> lbc =
- LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
- if (lbc != 0) {
- sp<Client> client(lbc->client.promote());
- snprintf(buffer, SIZE,
- " name=%s\n", lbc->getName().string());
- result.append(buffer);
- snprintf(buffer, SIZE,
- " id=0x%08x, client=0x%08x, identity=%u\n",
- lbc->clientIndex(), client.get() ? client->cid : 0,
- lbc->getIdentity());
-
- result.append(buffer);
- buffer[0] = 0;
- }
- /*** Layer ***/
- sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get());
- if (l != 0) {
- SharedBufferStack::Statistics stats = l->lcblk->getStats();
- result.append( l->lcblk->dump(" ") );
- sp<const GraphicBuffer> buf0(l->getBuffer(0));
- sp<const GraphicBuffer> buf1(l->getBuffer(1));
- uint32_t w0=0, h0=0, s0=0;
- uint32_t w1=0, h1=0, s1=0;
- if (buf0 != 0) {
- w0 = buf0->getWidth();
- h0 = buf0->getHeight();
- s0 = buf0->getStride();
- }
- if (buf1 != 0) {
- w1 = buf1->getWidth();
- h1 = buf1->getHeight();
- s1 = buf1->getStride();
- }
- snprintf(buffer, SIZE,
- " "
- "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u],"
- " freezeLock=%p, dq-q-time=%u us\n",
- l->pixelFormat(),
- w0, h0, s0, w1, h1, s1,
- l->getFreezeLock().get(), stats.totalTime);
- result.append(buffer);
- buffer[0] = 0;
- }
+ 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");
}
+
mWormholeRegion.dump(result, "WormholeRegion");
const DisplayHardware& hw(graphicPlane(0).displayHardware());
snprintf(buffer, SIZE,
@@ -1601,18 +1409,19 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
" last transaction time : %f us\n",
mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0);
result.append(buffer);
+
if (inSwapBuffersDuration || !locked) {
snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n",
inSwapBuffersDuration/1000.0);
result.append(buffer);
}
+
if (inTransactionDuration || !locked) {
snprintf(buffer, SIZE, " transaction time: %f us\n",
inTransactionDuration/1000.0);
result.append(buffer);
}
- snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size());
- result.append(buffer);
+
const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
alloc.dump(result);
@@ -1705,116 +1514,189 @@ status_t SurfaceFlinger::onTransact(
}
// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger)
- : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger)
+sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const
{
- const int pgsize = getpagesize();
- const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));
+ sp<Layer> result;
+ Mutex::Autolock _l(mStateLock);
+ result = mLayerMap.valueFor( sur->asBinder() ).promote();
+ return result;
+}
- mCblkHeap = new MemoryHeapBase(cblksize, 0,
- "SurfaceFlinger Client control-block");
+// ---------------------------------------------------------------------------
- ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());
- if (ctrlblk) { // construct the shared structure in-place.
- new(ctrlblk) SharedClient;
- }
+Client::Client(const sp<SurfaceFlinger>& flinger)
+ : mFlinger(flinger), mNameGenerator(1)
+{
}
-Client::~Client() {
- if (ctrlblk) {
- ctrlblk->~SharedClient(); // destroy our shared-structure.
+Client::~Client()
+{
+ const size_t count = mLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ sp<LayerBaseClient> layer(mLayers.valueAt(i).promote());
+ if (layer != 0) {
+ mFlinger->removeLayer(layer);
+ }
}
}
-int32_t Client::generateId(int pid)
-{
- const uint32_t i = clz( ~mBitmap );
- if (i >= NUM_LAYERS_MAX) {
- return NO_MEMORY;
- }
- mPid = pid;
- mInUse.add(uint8_t(i));
- mBitmap |= 1<<(31-i);
- return i;
+status_t Client::initCheck() const {
+ return NO_ERROR;
}
-status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id)
+ssize_t Client::attachLayer(const sp<LayerBaseClient>& layer)
{
- ssize_t idx = mInUse.indexOf(id);
- if (idx < 0)
- return NAME_NOT_FOUND;
- return mLayers.insertAt(layer, idx);
+ int32_t name = android_atomic_inc(&mNameGenerator);
+ mLayers.add(name, layer);
+ return name;
}
-void Client::free(int32_t id)
+void Client::detachLayer(const LayerBaseClient* layer)
{
- ssize_t idx = mInUse.remove(uint8_t(id));
- if (idx >= 0) {
- mBitmap &= ~(1<<(31-id));
- mLayers.removeItemsAt(idx);
+ // we do a linear search here, because this doesn't happen often
+ const size_t count = mLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ if (mLayers.valueAt(i) == layer) {
+ mLayers.removeItemsAt(i, 1);
+ break;
+ }
}
}
-
-bool Client::isValid(int32_t i) const {
- return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i)));
-}
-
sp<LayerBaseClient> Client::getLayerUser(int32_t i) const {
sp<LayerBaseClient> lbc;
- ssize_t idx = mInUse.indexOf(uint8_t(i));
- if (idx >= 0) {
- lbc = mLayers[idx].promote();
- LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx));
+ const wp<LayerBaseClient>& layer(mLayers.valueFor(i));
+ if (layer != 0) {
+ lbc = layer.promote();
+ LOGE_IF(lbc==0, "getLayerUser(name=%d) is dead", int(i));
}
return lbc;
}
-void Client::dump(const char* what)
+sp<IMemoryHeap> Client::getControlBlock() const {
+ return 0;
+}
+ssize_t Client::getTokenForSurface(const sp<ISurface>& sur) const {
+ return -1;
+}
+sp<ISurface> Client::createSurface(
+ ISurfaceComposerClient::surface_data_t* params, int pid,
+ const String8& name,
+ DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags)
{
+ return mFlinger->createSurface(this, pid, name, params,
+ display, w, h, format, flags);
+}
+status_t Client::destroySurface(SurfaceID sid) {
+ return mFlinger->removeSurface(this, sid);
+}
+status_t Client::setState(int32_t count, const layer_state_t* states) {
+ return mFlinger->setClientState(this, count, states);
}
// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk)
- : mId(cid), mFlinger(flinger), mCblk(cblk)
+UserClient::UserClient(const sp<SurfaceFlinger>& flinger)
+ : ctrlblk(0), mBitmap(0), mFlinger(flinger)
{
+ const int pgsize = getpagesize();
+ const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));
+
+ mCblkHeap = new MemoryHeapBase(cblksize, 0,
+ "SurfaceFlinger Client control-block");
+
+ ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());
+ if (ctrlblk) { // construct the shared structure in-place.
+ new(ctrlblk) SharedClient;
+ }
}
-BClient::~BClient() {
- // destroy all resources attached to this client
- mFlinger->destroyConnection(mId);
+UserClient::~UserClient()
+{
+ if (ctrlblk) {
+ ctrlblk->~SharedClient(); // destroy our shared-structure.
+ }
+
+ /*
+ * When a UserClient dies, it's unclear what to do exactly.
+ * We could go ahead and destroy all surfaces linked to that client
+ * however, it wouldn't be fair to the main Client
+ * (usually the the window-manager), which might want to re-target
+ * the layer to another UserClient.
+ * I think the best is to do nothing, or not much; in most cases the
+ * WM itself will go ahead and clean things up when it detects a client of
+ * his has died.
+ * The remaining question is what to display? currently we keep
+ * just keep the current buffer.
+ */
}
-sp<IMemoryHeap> BClient::getControlBlock() const {
- return mCblk;
+status_t UserClient::initCheck() const {
+ return ctrlblk == 0 ? NO_INIT : NO_ERROR;
}
-sp<ISurface> BClient::createSurface(
- ISurfaceFlingerClient::surface_data_t* params, int pid,
- const String8& name,
- DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
- uint32_t flags)
+void UserClient::detachLayer(const Layer* layer)
{
- return mFlinger->createSurface(mId, pid, name, params, display, w, h,
- format, flags);
+ int32_t name = layer->getToken();
+ if (name >= 0) {
+ int32_t mask = 1LU<<name;
+ if ((android_atomic_and(~mask, &mBitmap) & mask) == 0) {
+ LOGW("token %d wasn't marked as used %08x", name, int(mBitmap));
+ }
+ }
}
-status_t BClient::destroySurface(SurfaceID sid)
-{
- sid |= (mId << 16); // add the client-part to id
- return mFlinger->removeSurface(sid);
+sp<IMemoryHeap> UserClient::getControlBlock() const {
+ return mCblkHeap;
}
-status_t BClient::setState(int32_t count, const layer_state_t* states)
+ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const
{
- return mFlinger->setClientState(mId, count, states);
+ int32_t name = NAME_NOT_FOUND;
+ sp<Layer> layer(mFlinger->getLayer(sur));
+ if (layer == 0) return name;
+
+ // if this layer already has a token, just return it
+ name = layer->getToken();
+ if ((name >= 0) && (layer->getClient() == this))
+ return name;
+
+ name = 0;
+ do {
+ int32_t mask = 1LU<<name;
+ if ((android_atomic_or(mask, &mBitmap) & mask) == 0) {
+ // we found and locked that name
+ status_t err = layer->setToken(
+ const_cast<UserClient*>(this), ctrlblk, name);
+ if (err != NO_ERROR) {
+ // free the name
+ android_atomic_and(~mask, &mBitmap);
+ name = err;
+ }
+ break;
+ }
+ if (++name > 31)
+ name = NO_MEMORY;
+ } while(name >= 0);
+
+ //LOGD("getTokenForSurface(%p) => %d (client=%p, bitmap=%08lx)",
+ // sur->asBinder().get(), name, this, mBitmap);
+ return name;
+}
+
+sp<ISurface> UserClient::createSurface(
+ ISurfaceComposerClient::surface_data_t* params, int pid,
+ const String8& name,
+ DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags) {
+ return 0;
+}
+status_t UserClient::destroySurface(SurfaceID sid) {
+ return INVALID_OPERATION;
+}
+status_t UserClient::setState(int32_t count, const layer_state_t* states) {
+ return INVALID_OPERATION;
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d75dc15..8ecfc01 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -29,74 +29,96 @@
#include <binder/IMemory.h>
#include <binder/Permission.h>
+#include <binder/BinderService.h>
#include <ui/PixelFormat.h>
#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
#include "Barrier.h"
#include "Layer.h"
-#include "Tokenizer.h"
#include "MessageQueue.h"
-struct copybit_device_t;
-struct overlay_device_t;
-
namespace android {
// ---------------------------------------------------------------------------
class Client;
-class BClient;
class DisplayHardware;
class FreezeLock;
class Layer;
+class LayerBlur;
+class LayerDim;
class LayerBuffer;
-typedef int32_t ClientID;
-
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
// ---------------------------------------------------------------------------
-class Client : public RefBase
+class Client : public BnSurfaceComposerClient
+{
+public:
+ Client(const sp<SurfaceFlinger>& flinger);
+ ~Client();
+
+ status_t initCheck() const;
+
+ // protected by SurfaceFlinger::mStateLock
+ ssize_t attachLayer(const sp<LayerBaseClient>& layer);
+ void detachLayer(const LayerBaseClient* layer);
+ sp<LayerBaseClient> getLayerUser(int32_t i) const;
+
+private:
+
+ // ISurfaceComposerClient interface
+ virtual sp<IMemoryHeap> getControlBlock() const;
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const;
+ virtual sp<ISurface> createSurface(
+ surface_data_t* params, int pid, const String8& name,
+ DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
+ uint32_t flags);
+ virtual status_t destroySurface(SurfaceID surfaceId);
+ virtual status_t setState(int32_t count, const layer_state_t* states);
+
+ DefaultKeyedVector< size_t, wp<LayerBaseClient> > mLayers;
+ sp<SurfaceFlinger> mFlinger;
+ int32_t mNameGenerator;
+};
+
+class UserClient : public BnSurfaceComposerClient
{
public:
- Client(ClientID cid, const sp<SurfaceFlinger>& flinger);
- ~Client();
-
- int32_t generateId(int pid);
- void free(int32_t id);
- status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id);
-
- inline bool isValid(int32_t i) const;
- sp<LayerBaseClient> getLayerUser(int32_t i) const;
- void dump(const char* what);
-
- const Vector< wp<LayerBaseClient> >& getLayers() const {
- return mLayers;
- }
-
- const sp<IMemoryHeap>& getControlBlockMemory() const {
- return mCblkHeap;
- }
-
// pointer to this client's control block
- SharedClient* ctrlblk;
- ClientID cid;
+ SharedClient* ctrlblk;
+
+public:
+ UserClient(const sp<SurfaceFlinger>& flinger);
+ ~UserClient();
+
+ status_t initCheck() const;
+
+ // protected by SurfaceFlinger::mStateLock
+ void detachLayer(const Layer* layer);
-
private:
- int getClientPid() const { return mPid; }
-
- int mPid;
- uint32_t mBitmap;
- SortedVector<uint8_t> mInUse;
- Vector< wp<LayerBaseClient> > mLayers;
- sp<IMemoryHeap> mCblkHeap;
- sp<SurfaceFlinger> mFlinger;
+
+ // ISurfaceComposerClient interface
+ virtual sp<IMemoryHeap> getControlBlock() const;
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const;
+ virtual sp<ISurface> createSurface(
+ surface_data_t* params, int pid, const String8& name,
+ DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
+ uint32_t flags);
+ virtual status_t destroySurface(SurfaceID surfaceId);
+ virtual status_t setState(int32_t count, const layer_state_t* states);
+
+ // atomic-ops
+ mutable volatile int32_t mBitmap;
+
+ sp<IMemoryHeap> mCblkHeap;
+ sp<SurfaceFlinger> mFlinger;
};
// ---------------------------------------------------------------------------
@@ -143,11 +165,13 @@ enum {
eTraversalNeeded = 0x02
};
-class SurfaceFlinger : public BnSurfaceComposer, protected Thread
+class SurfaceFlinger :
+ public BinderService<SurfaceFlinger>,
+ public BnSurfaceComposer,
+ protected Thread
{
public:
- static void instantiate();
- static void shutdown();
+ static char const* getServiceName() { return "SurfaceFlinger"; }
SurfaceFlinger();
virtual ~SurfaceFlinger();
@@ -159,7 +183,8 @@ public:
virtual status_t dump(int fd, const Vector<String16>& args);
// ISurfaceComposer interface
- virtual sp<ISurfaceFlingerClient> createConnection();
+ virtual sp<ISurfaceComposerClient> createConnection();
+ virtual sp<ISurfaceComposerClient> createClientConnection();
virtual sp<IMemoryHeap> getCblk() const;
virtual void bootFinished();
virtual void openGlobalTransaction();
@@ -174,13 +199,14 @@ public:
overlay_control_device_t* getOverlayEngine() const;
-
status_t removeLayer(const sp<LayerBase>& layer);
status_t addLayer(const sp<LayerBase>& layer);
status_t invalidateLayerVisibility(const sp<LayerBase>& layer);
-
+
+ sp<Layer> getLayer(const sp<ISurface>& sur) const;
+
private:
- friend class BClient;
+ friend class Client;
friend class LayerBase;
friend class LayerBuffer;
friend class LayerBaseClient;
@@ -189,47 +215,47 @@ private:
friend class LayerBlur;
friend class LayerDim;
- sp<ISurface> createSurface(ClientID client, int pid, const String8& name,
- ISurfaceFlingerClient::surface_data_t* params,
+ sp<ISurface> createSurface(const sp<Client>& client,
+ int pid, const String8& name,
+ ISurfaceComposerClient::surface_data_t* params,
DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags);
- sp<LayerBaseClient> createNormalSurfaceLocked(
+ sp<Layer> createNormalSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ uint32_t w, uint32_t h, uint32_t flags,
PixelFormat& format);
- sp<LayerBaseClient> createBlurSurfaceLocked(
+ sp<LayerBlur> createBlurSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- sp<LayerBaseClient> createDimSurfaceLocked(
+ sp<LayerDim> createDimSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- sp<LayerBaseClient> createPushBuffersSurfaceLocked(
+ sp<LayerBuffer> createPushBuffersSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- status_t removeSurface(SurfaceID surface_id);
+ status_t removeSurface(const sp<Client>& client, SurfaceID sid);
status_t destroySurface(const sp<LayerBaseClient>& layer);
- status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states);
+ status_t setClientState(const sp<Client>& client,
+ int32_t count, const layer_state_t* states);
-
- class LayerVector {
+ class LayerVector : public SortedVector< sp<LayerBase> > {
public:
- inline LayerVector() { }
- LayerVector(const LayerVector&);
- inline size_t size() const { return layers.size(); }
- inline sp<LayerBase> const* array() const { return layers.array(); }
- ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
- ssize_t remove(const sp<LayerBase>&);
- ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
- ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const;
- inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; }
- private:
- KeyedVector< sp<LayerBase> , size_t> lookup;
- Vector< sp<LayerBase> > layers;
+ LayerVector() { }
+ LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }
+ virtual int do_compare(const void* lhs, const void* rhs) const {
+ const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));
+ const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));
+ // sort layers by Z order
+ uint32_t lz = l->currentState().z;
+ uint32_t rz = r->currentState().z;
+ // then by sequence, so we get a stable ordering
+ return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);
+ }
};
struct State {
@@ -256,8 +282,6 @@ private:
public: // hack to work around gcc 4.0.3 bug
void signalEvent();
private:
- void signalDelayedEvent(nsecs_t delay);
-
void handleConsoleEvents();
void handleTransaction(uint32_t transactionFlags);
void handleTransactionLocked(
@@ -278,15 +302,14 @@ private:
void unlockClients();
- void destroyConnection(ClientID cid);
- sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const;
+ ssize_t addClientLayer(const sp<Client>& client,
+ const sp<LayerBaseClient>& lbc);
status_t addLayer_l(const sp<LayerBase>& layer);
status_t removeLayer_l(const sp<LayerBase>& layer);
status_t purgatorizeLayer_l(const sp<LayerBase>& layer);
- void free_resources_l();
uint32_t getTransactionFlags(uint32_t flags);
- uint32_t setTransactionFlags(uint32_t flags, nsecs_t delay = 0);
+ uint32_t setTransactionFlags(uint32_t flags);
void commitTransaction();
@@ -310,9 +333,13 @@ private:
mutable MessageQueue mEventQueue;
-
-
-
+
+ status_t postMessageAsync(const sp<MessageBase>& msg,
+ nsecs_t reltime=0, uint32_t flags = 0);
+
+ status_t postMessageSync(const sp<MessageBase>& msg,
+ nsecs_t reltime=0, uint32_t flags = 0);
+
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState;
@@ -321,14 +348,11 @@ private:
volatile int32_t mTransactionCount;
Condition mTransactionCV;
bool mResizeTransationPending;
-
+
// protected by mStateLock (but we could use another lock)
- Tokenizer mTokens;
- DefaultKeyedVector<ClientID, sp<Client> > mClientsMap;
- DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap;
- GraphicPlane mGraphicPlanes[1];
- bool mLayersRemoved;
- Vector< sp<Client> > mDisconnectedClients;
+ GraphicPlane mGraphicPlanes[1];
+ bool mLayersRemoved;
+ DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayerMap;
// constant members (no synchronization needed for access)
sp<IMemoryHeap> mServerHeap;
@@ -350,6 +374,8 @@ private:
bool mFreezeDisplay;
int32_t mFreezeCount;
nsecs_t mFreezeDisplayTime;
+ Vector< sp<LayerBase> > mVisibleLayersSortedByZ;
+
// don't use a lock for these, we don't care
int mDebugRegion;
@@ -389,32 +415,6 @@ public:
};
// ---------------------------------------------------------------------------
-
-class BClient : public BnSurfaceFlingerClient
-{
-public:
- BClient(SurfaceFlinger *flinger, ClientID cid,
- const sp<IMemoryHeap>& cblk);
- ~BClient();
-
- // ISurfaceFlingerClient interface
- virtual sp<IMemoryHeap> getControlBlock() const;
-
- virtual sp<ISurface> createSurface(
- surface_data_t* params, int pid, const String8& name,
- DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
- uint32_t flags);
-
- virtual status_t destroySurface(SurfaceID surfaceId);
- virtual status_t setState(int32_t count, const layer_state_t* states);
-
-private:
- ClientID mId;
- SurfaceFlinger* mFlinger;
- sp<IMemoryHeap> mCblk;
-};
-
-// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SURFACE_FLINGER_H
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
new file mode 100644
index 0000000..76f6159
--- /dev/null
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -0,0 +1,341 @@
+/*
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <hardware/hardware.h>
+
+#include "clz.h"
+#include "DisplayHardware/DisplayHardware.h"
+#include "GLExtensions.h"
+#include "TextureManager.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+TextureManager::TextureManager()
+ : mGLExtensions(GLExtensions::getInstance())
+{
+}
+
+GLenum TextureManager::getTextureTarget(const Image* image) {
+#if defined(GL_OES_texture_external)
+ switch (image->target) {
+ case Texture::TEXTURE_EXTERNAL:
+ return GL_TEXTURE_EXTERNAL_OES;
+ }
+#endif
+ return GL_TEXTURE_2D;
+}
+
+status_t TextureManager::initTexture(Texture* texture)
+{
+ if (texture->name != -1UL)
+ return INVALID_OPERATION;
+
+ GLuint textureName = -1;
+ glGenTextures(1, &textureName);
+ texture->name = textureName;
+ texture->width = 0;
+ texture->height = 0;
+
+ const GLenum target = GL_TEXTURE_2D;
+ glBindTexture(target, textureName);
+ glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ return NO_ERROR;
+}
+
+status_t TextureManager::initTexture(Image* pImage, int32_t format)
+{
+ if (pImage->name != -1UL)
+ return INVALID_OPERATION;
+
+ GLuint textureName = -1;
+ glGenTextures(1, &textureName);
+ pImage->name = textureName;
+ pImage->width = 0;
+ pImage->height = 0;
+
+ GLenum target = GL_TEXTURE_2D;
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ if (format && isYuvFormat(format)) {
+ target = GL_TEXTURE_EXTERNAL_OES;
+ pImage->target = Texture::TEXTURE_EXTERNAL;
+ }
+ }
+#endif
+
+ glBindTexture(target, textureName);
+ glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ return NO_ERROR;
+}
+
+bool TextureManager::isSupportedYuvFormat(int format)
+{
+ switch (format) {
+ case HAL_PIXEL_FORMAT_YV12:
+ return true;
+ }
+ return false;
+}
+
+bool TextureManager::isYuvFormat(int format)
+{
+ switch (format) {
+ // supported YUV formats
+ case HAL_PIXEL_FORMAT_YV12:
+ // Legacy/deprecated YUV formats
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ return true;
+ }
+
+ // Any OEM format needs to be considered
+ if (format>=0x100 && format<=0x1FF)
+ return true;
+
+ return false;
+}
+
+status_t TextureManager::initEglImage(Image* pImage,
+ EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
+{
+ status_t err = NO_ERROR;
+ if (!pImage->dirty) return err;
+
+ // free the previous image
+ if (pImage->image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(dpy, pImage->image);
+ pImage->image = EGL_NO_IMAGE_KHR;
+ }
+
+ // construct an EGL_NATIVE_BUFFER_ANDROID
+ android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
+
+ // create the new EGLImageKHR
+ const EGLint attrs[] = {
+ EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+ EGL_NONE, EGL_NONE
+ };
+ pImage->image = eglCreateImageKHR(
+ dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ (EGLClientBuffer)clientBuf, attrs);
+
+ if (pImage->image != EGL_NO_IMAGE_KHR) {
+ if (pImage->name == -1UL) {
+ initTexture(pImage, buffer->format);
+ }
+ const GLenum target = getTextureTarget(pImage);
+ glBindTexture(target, pImage->name);
+ glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
+ GLint error = glGetError();
+ if (error != GL_NO_ERROR) {
+ LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
+ pImage->image, error);
+ err = INVALID_OPERATION;
+ } else {
+ // Everything went okay!
+ pImage->dirty = false;
+ pImage->width = clientBuf->width;
+ pImage->height = clientBuf->height;
+ }
+ } else {
+ LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
+ err = INVALID_OPERATION;
+ }
+ return err;
+}
+
+status_t TextureManager::loadTexture(Texture* texture,
+ const Region& dirty, const GGLSurface& t)
+{
+ if (texture->name == -1UL) {
+ status_t err = initTexture(texture);
+ LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
+ return err;
+ }
+
+ if (texture->target != Texture::TEXTURE_2D)
+ return INVALID_OPERATION;
+
+ glBindTexture(GL_TEXTURE_2D, texture->name);
+
+ /*
+ * In OpenGL ES we can't specify a stride with glTexImage2D (however,
+ * GL_UNPACK_ALIGNMENT is a limited form of stride).
+ * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
+ * need to do something reasonable (here creating a bigger texture).
+ *
+ * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
+ *
+ * This situation doesn't happen often, but some h/w have a limitation
+ * for their framebuffer (eg: must be multiple of 8 pixels), and
+ * we need to take that into account when using these buffers as
+ * textures.
+ *
+ * This should never be a problem with POT textures
+ */
+
+ int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
+ unpack = 1 << ((unpack > 3) ? 3 : unpack);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
+
+ /*
+ * round to POT if needed
+ */
+ if (!mGLExtensions.haveNpot()) {
+ texture->NPOTAdjust = true;
+ }
+
+ if (texture->NPOTAdjust) {
+ // find the smallest power-of-two that will accommodate our surface
+ texture->potWidth = 1 << (31 - clz(t.width));
+ texture->potHeight = 1 << (31 - clz(t.height));
+ if (texture->potWidth < t.width) texture->potWidth <<= 1;
+ if (texture->potHeight < t.height) texture->potHeight <<= 1;
+ texture->wScale = float(t.width) / texture->potWidth;
+ texture->hScale = float(t.height) / texture->potHeight;
+ } else {
+ texture->potWidth = t.width;
+ texture->potHeight = t.height;
+ }
+
+ Rect bounds(dirty.bounds());
+ GLvoid* data = 0;
+ if (texture->width != t.width || texture->height != t.height) {
+ texture->width = t.width;
+ texture->height = t.height;
+
+ // texture size changed, we need to create a new one
+ bounds.set(Rect(t.width, t.height));
+ if (t.width == texture->potWidth &&
+ t.height == texture->potHeight) {
+ // we can do it one pass
+ data = t.data;
+ }
+
+ if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGB, texture->potWidth, texture->potHeight, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
+ } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGBA, texture->potWidth, texture->potHeight, 0,
+ GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
+ } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
+ t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGBA, texture->potWidth, texture->potHeight, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, data);
+ } else if (isSupportedYuvFormat(t.format)) {
+ // just show the Y plane of YUV buffers
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
+ } else {
+ // oops, we don't handle this format!
+ LOGE("texture=%d, using format %d, which is not "
+ "supported by the GL", texture->name, t.format);
+ }
+ }
+ if (!data) {
+ if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
+ t.data + bounds.top*t.stride*2);
+ } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
+ t.data + bounds.top*t.stride*2);
+ } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
+ t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ t.data + bounds.top*t.stride*4);
+ } else if (isSupportedYuvFormat(t.format)) {
+ // just show the Y plane of YUV buffers
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_LUMINANCE, GL_UNSIGNED_BYTE,
+ t.data + bounds.top*t.stride);
+ }
+ }
+ return NO_ERROR;
+}
+
+void TextureManager::activateTexture(const Texture& texture, bool filter)
+{
+ const GLenum target = getTextureTarget(&texture);
+ if (target == GL_TEXTURE_2D) {
+ glBindTexture(GL_TEXTURE_2D, texture.name);
+ glEnable(GL_TEXTURE_2D);
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+ } else {
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
+ glEnable(GL_TEXTURE_EXTERNAL_OES);
+ glDisable(GL_TEXTURE_2D);
+#endif
+ }
+
+ if (filter) {
+ glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+}
+
+void TextureManager::deactivateTextures()
+{
+ glDisable(GL_TEXTURE_2D);
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
new file mode 100644
index 0000000..18c4348
--- /dev/null
+++ b/services/surfaceflinger/TextureManager.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_TEXTURE_MANAGER_H
+#define ANDROID_TEXTURE_MANAGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+
+#include <ui/Region.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class GLExtensions;
+class GraphicBuffer;
+
+// ---------------------------------------------------------------------------
+
+struct Image {
+ enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 };
+ Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0),
+ dirty(1), target(TEXTURE_2D) { }
+ GLuint name;
+ EGLImageKHR image;
+ GLuint width;
+ GLuint height;
+ unsigned dirty : 1;
+ unsigned target : 1;
+};
+
+struct Texture : public Image {
+ Texture() : Image(), NPOTAdjust(0) { }
+ GLuint potWidth;
+ GLuint potHeight;
+ GLfloat wScale;
+ GLfloat hScale;
+ unsigned NPOTAdjust : 1;
+};
+
+// ---------------------------------------------------------------------------
+
+class TextureManager {
+ const GLExtensions& mGLExtensions;
+ static status_t initTexture(Image* texture, int32_t format);
+ static status_t initTexture(Texture* texture);
+ static bool isSupportedYuvFormat(int format);
+ static bool isYuvFormat(int format);
+ static GLenum getTextureTarget(const Image* pImage);
+public:
+
+ TextureManager();
+
+ // load bitmap data into the active buffer
+ status_t loadTexture(Texture* texture,
+ const Region& dirty, const GGLSurface& t);
+
+ // make active buffer an EGLImage if needed
+ status_t initEglImage(Image* texture,
+ EGLDisplay dpy, const sp<GraphicBuffer>& buffer);
+
+ // activate a texture
+ static void activateTexture(const Texture& texture, bool filter);
+
+ // deactivate a texture
+ static void deactivateTextures();
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_TEXTURE_MANAGER_H
diff --git a/services/surfaceflinger/Tokenizer.cpp b/services/surfaceflinger/Tokenizer.cpp
deleted file mode 100644
index be3a239..0000000
--- a/services/surfaceflinger/Tokenizer.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdio.h>
-
-#include "Tokenizer.h"
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t)
-
-Tokenizer::Tokenizer()
-{
-}
-
-Tokenizer::Tokenizer(const Tokenizer& other)
- : mRanges(other.mRanges)
-{
-}
-
-Tokenizer::~Tokenizer()
-{
-}
-
-uint32_t Tokenizer::acquire()
-{
- if (!mRanges.size() || mRanges[0].first) {
- _insertTokenAt(0,0);
- return 0;
- }
-
- // just extend the first run
- const run_t& run = mRanges[0];
- uint32_t token = run.first + run.length;
- _insertTokenAt(token, 1);
- return token;
-}
-
-bool Tokenizer::isAcquired(uint32_t token) const
-{
- return (_indexOrderOf(token) >= 0);
-}
-
-status_t Tokenizer::reserve(uint32_t token)
-{
- size_t o;
- const ssize_t i = _indexOrderOf(token, &o);
- if (i >= 0) {
- return BAD_VALUE; // this token is already taken
- }
- ssize_t err = _insertTokenAt(token, o);
- return (err<0) ? err : status_t(NO_ERROR);
-}
-
-status_t Tokenizer::release(uint32_t token)
-{
- const ssize_t i = _indexOrderOf(token);
- if (i >= 0) {
- const run_t& run = mRanges[i];
- if ((token >= run.first) && (token < run.first+run.length)) {
- // token in this range, we need to split
- run_t& run = mRanges.editItemAt(i);
- if ((token == run.first) || (token == run.first+run.length-1)) {
- if (token == run.first) {
- run.first += 1;
- }
- run.length -= 1;
- if (run.length == 0) {
- // XXX: should we systematically remove a run that's empty?
- mRanges.removeItemsAt(i);
- }
- } else {
- // split the run
- run_t new_run;
- new_run.first = token+1;
- new_run.length = run.first+run.length - new_run.first;
- run.length = token - run.first;
- mRanges.insertAt(new_run, i+1);
- }
- return NO_ERROR;
- }
- }
- return NAME_NOT_FOUND;
-}
-
-ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const
-{
- // binary search
- ssize_t err = NAME_NOT_FOUND;
- ssize_t l = 0;
- ssize_t h = mRanges.size()-1;
- ssize_t mid;
- const run_t* a = mRanges.array();
- while (l <= h) {
- mid = l + (h - l)/2;
- const run_t* const curr = a + mid;
- int c = 0;
- if (token < curr->first) c = 1;
- else if (token >= curr->first+curr->length) c = -1;
- if (c == 0) {
- err = l = mid;
- break;
- } else if (c < 0) {
- l = mid + 1;
- } else {
- h = mid - 1;
- }
- }
- if (order) *order = l;
- return err;
-}
-
-ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index)
-{
- const size_t c = mRanges.size();
-
- if (index >= 1) {
- // do we need to merge with the previous run?
- run_t& p = mRanges.editItemAt(index-1);
- if (p.first+p.length == token) {
- p.length += 1;
- if (index < c) {
- const run_t& n = mRanges[index];
- if (token+1 == n.first) {
- p.length += n.length;
- mRanges.removeItemsAt(index);
- }
- }
- return index;
- }
- }
-
- if (index < c) {
- // do we need to merge with the next run?
- run_t& n = mRanges.editItemAt(index);
- if (token+1 == n.first) {
- n.first -= 1;
- n.length += 1;
- return index;
- }
- }
-
- return mRanges.insertAt(run_t(token,1), index);
-}
-
-void Tokenizer::dump() const
-{
- const run_t* ranges = mRanges.array();
- const size_t c = mRanges.size();
- printf("Tokenizer (%p, size = %d)\n", this, int(c));
- for (size_t i=0 ; i<c ; i++) {
- printf("%u: (%u, %u)\n", i,
- uint32_t(ranges[i].first), uint32_t(ranges[i].length));
- }
-}
-
-}; // namespace android
-
diff --git a/services/surfaceflinger/Tokenizer.h b/services/surfaceflinger/Tokenizer.h
deleted file mode 100644
index 6b3057d..0000000
--- a/services/surfaceflinger/Tokenizer.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2007 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_TOKENIZER_H
-#define ANDROID_TOKENIZER_H
-
-#include <utils/Vector.h>
-#include <utils/Errors.h>
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-class Tokenizer
-{
-public:
- Tokenizer();
- Tokenizer(const Tokenizer& other);
- ~Tokenizer();
-
- uint32_t acquire();
- status_t reserve(uint32_t token);
- status_t release(uint32_t token);
- bool isAcquired(uint32_t token) const;
-
- void dump() const;
-
- struct run_t {
- run_t() {};
- run_t(uint32_t f, uint32_t l) : first(f), length(l) {}
- uint32_t first;
- uint32_t length;
- };
-private:
- ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const;
- ssize_t _insertTokenAt(uint32_t token, size_t index);
- Vector<run_t> mRanges;
-};
-
-}; // namespace android
-
-// ----------------------------------------------------------------------------
-
-#endif // ANDROID_TOKENIZER_H
diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
index 175f989..5e27cc9 100644
--- a/services/surfaceflinger/Transform.cpp
+++ b/services/surfaceflinger/Transform.cpp
@@ -229,14 +229,13 @@ Transform::vec3 Transform::transform(const vec3& v) const {
return r;
}
-void Transform::transform(fixed1616* point, int x, int y) const
+void Transform::transform(float* point, int x, int y) const
{
- const float toFixed = 65536.0f;
const mat33& M(mMatrix);
vec2 v(x, y);
v = transform(v);
- point[0] = v[0] * toFixed;
- point[1] = v[1] * toFixed;
+ point[0] = v[0];
+ point[1] = v[1];
}
Rect Transform::makeBounds(int w, int h) const
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index 2e5b893..20fa11a 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -37,8 +37,6 @@ public:
explicit Transform(uint32_t orientation);
~Transform();
- typedef int32_t fixed1616;
-
// FIXME: must match OVERLAY_TRANSFORM_*, pull from hardware.h
enum orientation_flags {
ROT_0 = 0x00000000,
@@ -76,7 +74,7 @@ public:
// transform data
Rect makeBounds(int w, int h) const;
- void transform(fixed1616* point, int x, int y) const;
+ void transform(float* point, int x, int y) const;
Region transform(const Region& reg) const;
Transform operator * (const Transform& rhs) const;
diff --git a/services/surfaceflinger/tests/surface/Android.mk b/services/surfaceflinger/tests/surface/Android.mk
new file mode 100644
index 0000000..ce0e807
--- /dev/null
+++ b/services/surfaceflinger/tests/surface/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ surface.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libui \
+ libsurfaceflinger_client
+
+LOCAL_MODULE:= test-surface
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
new file mode 100644
index 0000000..b4de4b4
--- /dev/null
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -0,0 +1,54 @@
+#include <cutils/memory.h>
+
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include <ui/Overlay.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ // set up the thread-pool
+ sp<ProcessState> proc(ProcessState::self());
+ ProcessState::self()->startThreadPool();
+
+ // create a client to surfaceflinger
+ sp<SurfaceComposerClient> client = new SurfaceComposerClient();
+
+ // create pushbuffer surface
+ sp<SurfaceControl> surfaceControl = client->createSurface(
+ getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565);
+ client->openTransaction();
+ surfaceControl->setLayer(100000);
+ client->closeTransaction();
+
+ // pretend it went cross-process
+ Parcel parcel;
+ SurfaceControl::writeSurfaceToParcel(surfaceControl, &parcel);
+ parcel.setDataPosition(0);
+ sp<Surface> surface = Surface::readFromParcel(parcel);
+ ANativeWindow* window = surface.get();
+
+ printf("window=%p\n", window);
+
+ int err = native_window_set_buffer_count(window, 8);
+ android_native_buffer_t* buffer;
+
+ for (int i=0 ; i<8 ; i++) {
+ window->dequeueBuffer(window, &buffer);
+ printf("buffer %d: %p\n", i, buffer);
+ }
+
+ printf("test complete. CTRL+C to finish.\n");
+
+ IPCThreadState::self()->joinThreadPool();
+ return 0;
+}
diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
index 78a90fb..f3baff4 100644
--- a/services/tests/servicestests/src/com/android/server/DropBoxTest.java
+++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
@@ -20,6 +20,10 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.DropBoxManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.ServiceManager;
import android.os.StatFs;
import android.provider.Settings;
@@ -27,10 +31,13 @@ import android.test.AndroidTestCase;
import com.android.server.DropBoxManagerService;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.Random;
import java.util.zip.GZIPOutputStream;
@@ -531,6 +538,203 @@ public class DropBoxTest extends AndroidTestCase {
service.stop();
}
+ public void testDropBoxEntrySerialization() throws Exception {
+ // Make sure DropBoxManager.Entry can be serialized to a Parcel and back
+ // under a variety of conditions.
+
+ Parcel parcel = Parcel.obtain();
+ File dir = getEmptyDir("testDropBoxEntrySerialization");
+
+ new DropBoxManager.Entry("empty", 1000000).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("string", 2000000, "String Value").writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("bytes", 3000000, "Bytes Value".getBytes(),
+ DropBoxManager.IS_TEXT).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("zerobytes", 4000000, new byte[0], 0).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("emptybytes", 5000000, (byte[]) null,
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+
+ try {
+ new DropBoxManager.Entry("badbytes", 99999,
+ "Bad Bytes Value".getBytes(),
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+ fail("IllegalArgumentException expected for non-null byte[] and IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badbytes", 99999, (byte[]) null, 0).writeToParcel(parcel, 0);
+ fail("IllegalArgumentException expected for null byte[] and non-IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ File f = new File(dir, "file.dat");
+ FileOutputStream os = new FileOutputStream(f);
+ os.write("File Value".getBytes());
+ os.close();
+
+ new DropBoxManager.Entry("file", 6000000, f, DropBoxManager.IS_TEXT).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("binfile", 7000000, f, 0).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("emptyfile", 8000000, (ParcelFileDescriptor) null,
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, new File(dir, "nonexist.dat"), 0);
+ fail("IOException expected for nonexistent file");
+ } catch (IOException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, f, DropBoxManager.IS_EMPTY).writeToParcel(
+ parcel, 0);
+ fail("IllegalArgumentException expected for non-null file and IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, (ParcelFileDescriptor) null, 0);
+ fail("IllegalArgumentException expected for null PFD and non-IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ File gz = new File(dir, "file.gz");
+ GZIPOutputStream gzout = new GZIPOutputStream(new FileOutputStream(gz));
+ gzout.write("Gzip File Value".getBytes());
+ gzout.close();
+
+ new DropBoxManager.Entry("gzipfile", 9000000, gz,
+ DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("gzipbinfile", 10000000, gz,
+ DropBoxManager.IS_GZIPPED).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+ //
+ // Switch from writing to reading
+ //
+
+ parcel.setDataPosition(0);
+ DropBoxManager.Entry e;
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("empty", e.getTag());
+ assertEquals(1000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("string", e.getTag());
+ assertEquals(2000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("String Value", e.getText(100));
+ assertEquals("String Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("bytes", e.getTag());
+ assertEquals(3000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("Bytes Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("zerobytes", e.getTag());
+ assertEquals(4000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null,
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("emptybytes", e.getTag());
+ assertEquals(5000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("file", e.getTag());
+ assertEquals(6000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("File Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("binfile", e.getTag());
+ assertEquals(7000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals("File Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("emptyfile", e.getTag());
+ assertEquals(8000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("gzipfile", e.getTag());
+ assertEquals(9000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("Gzip File Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("gzipbinfile", e.getTag());
+ assertEquals(10000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals("Gzip File Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ assertEquals(0, parcel.dataAvail());
+ parcel.recycle();
+ }
+
+ public void testDropBoxEntrySerializationDoesntLeakFileDescriptors() throws Exception {
+ File dir = getEmptyDir("testDropBoxEntrySerialization");
+ File f = new File(dir, "file.dat");
+ FileOutputStream os = new FileOutputStream(f);
+ os.write("File Value".getBytes());
+ os.close();
+
+ int before = countOpenFiles();
+ assertTrue(before > 0);
+
+ for (int i = 0; i < 1000; i++) {
+ Parcel parcel = Parcel.obtain();
+ new DropBoxManager.Entry("file", 1000000, f, 0).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+ parcel.setDataPosition(0);
+ DropBoxManager.Entry e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("file", e.getTag());
+ e.close();
+
+ parcel.recycle();
+ }
+
+ int after = countOpenFiles();
+ assertTrue(after > 0);
+ assertTrue(after < before + 20);
+ }
+
private void addRandomEntry(DropBoxManager dropbox, String tag, int size) throws Exception {
byte[] bytes = new byte[size];
new Random(System.currentTimeMillis()).nextBytes(bytes);
@@ -564,4 +768,8 @@ public class DropBoxTest extends AndroidTestCase {
assertTrue(dir.listFiles().length == 0);
return dir;
}
+
+ private int countOpenFiles() {
+ return new File("/proc/" + Process.myPid() + "/fd").listFiles().length;
+ }
}