summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/A2dpAudioInterface.cpp498
-rw-r--r--services/audioflinger/A2dpAudioInterface.h138
-rw-r--r--services/audioflinger/Android.mk90
-rw-r--r--services/audioflinger/AudioDumpInterface.cpp573
-rw-r--r--services/audioflinger/AudioDumpInterface.h170
-rw-r--r--services/audioflinger/AudioFlinger.cpp543
-rw-r--r--services/audioflinger/AudioFlinger.h37
-rw-r--r--services/audioflinger/AudioHardwareGeneric.cpp411
-rw-r--r--services/audioflinger/AudioHardwareGeneric.h151
-rw-r--r--services/audioflinger/AudioHardwareInterface.cpp183
-rw-r--r--services/audioflinger/AudioHardwareStub.cpp209
-rw-r--r--services/audioflinger/AudioHardwareStub.h106
-rw-r--r--services/audioflinger/AudioPolicyManagerBase.cpp2286
-rw-r--r--services/audioflinger/AudioPolicyService.cpp613
-rw-r--r--services/audioflinger/AudioPolicyService.h90
-rw-r--r--services/camera/libcameraservice/Android.mk47
-rw-r--r--services/camera/libcameraservice/CameraHardwareInterface.h619
-rw-r--r--services/camera/libcameraservice/CameraService.cpp126
-rw-r--r--services/camera/libcameraservice/CameraService.h17
-rw-r--r--services/camera/tests/CameraServiceTest/Android.mk2
-rw-r--r--services/camera/tests/CameraServiceTest/CameraServiceTest.cpp8
-rw-r--r--services/input/Android.mk5
-rw-r--r--services/input/EventHub.cpp205
-rw-r--r--services/input/EventHub.h21
-rw-r--r--services/input/InputDispatcher.cpp848
-rw-r--r--services/input/InputDispatcher.h147
-rw-r--r--services/input/InputReader.cpp1980
-rw-r--r--services/input/InputReader.h234
-rw-r--r--services/input/InputWindow.h6
-rw-r--r--services/input/PointerController.cpp485
-rw-r--r--services/input/PointerController.h210
-rw-r--r--services/input/SpriteController.cpp488
-rw-r--r--services/input/SpriteController.h295
-rw-r--r--services/input/tests/Android.mk1
-rw-r--r--services/input/tests/InputDispatcher_test.cpp28
-rw-r--r--services/input/tests/InputReader_test.cpp42
-rw-r--r--services/java/com/android/server/AppWidgetService.java4
-rw-r--r--services/java/com/android/server/BackupManagerService.java512
-rw-r--r--services/java/com/android/server/BatteryService.java2
-rw-r--r--services/java/com/android/server/ConnectivityService.java12
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java27
-rw-r--r--services/java/com/android/server/EntropyService.java4
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java18
-rw-r--r--services/java/com/android/server/IntentResolver.java3
-rw-r--r--services/java/com/android/server/LightsService.java3
-rw-r--r--services/java/com/android/server/MountService.java8
-rw-r--r--services/java/com/android/server/NetworkManagementService.java74
-rw-r--r--services/java/com/android/server/ProcessStats.java3
-rw-r--r--services/java/com/android/server/RandomBlock.java4
-rw-r--r--services/java/com/android/server/StatusBarManagerService.java5
-rw-r--r--services/java/com/android/server/SystemBackupAgent.java20
-rw-r--r--services/java/com/android/server/SystemServer.java2
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java1
-rw-r--r--services/java/com/android/server/ThrottleService.java197
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java12
-rw-r--r--services/java/com/android/server/Watchdog.java3
-rw-r--r--services/java/com/android/server/WifiService.java13
-rw-r--r--services/java/com/android/server/WifiWatchdogService.java62
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityInputFilter.java111
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java (renamed from services/java/com/android/server/AccessibilityManagerService.java)101
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java1540
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java1053
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java79
-rw-r--r--services/java/com/android/server/am/ActivityStack.java270
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java13
-rw-r--r--services/java/com/android/server/am/ContentProviderRecord.java2
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java6
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java26
-rw-r--r--services/java/com/android/server/am/TaskAccessInfo.java35
-rw-r--r--services/java/com/android/server/am/TaskRecord.java4
-rw-r--r--services/java/com/android/server/am/ThumbnailHolder.java24
-rw-r--r--services/java/com/android/server/am/TransferPipe.java242
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java159
-rwxr-xr-xservices/java/com/android/server/location/ComprehensiveCountryDetector.java30
-rw-r--r--services/java/com/android/server/location/GpsXtraDownloader.java1
-rwxr-xr-xservices/java/com/android/server/location/LocationBasedCountryDetector.java88
-rw-r--r--services/java/com/android/server/pm/BasePermission.java59
-rw-r--r--services/java/com/android/server/pm/GrantedPermissions.java50
-rw-r--r--services/java/com/android/server/pm/Installer.java (renamed from services/java/com/android/server/Installer.java)272
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java (renamed from services/java/com/android/server/PackageManagerService.java)4875
-rw-r--r--services/java/com/android/server/pm/PackageSetting.java55
-rw-r--r--services/java/com/android/server/pm/PackageSettingBase.java207
-rw-r--r--services/java/com/android/server/pm/PackageSignatures.java198
-rw-r--r--services/java/com/android/server/pm/PendingPackage.java30
-rw-r--r--services/java/com/android/server/pm/PreferredActivity.java73
-rw-r--r--services/java/com/android/server/pm/Settings.java2230
-rw-r--r--services/java/com/android/server/pm/SharedUserSetting.java43
-rw-r--r--services/java/com/android/server/pm/UserManager.java401
-rw-r--r--services/java/com/android/server/wm/DimSurface.java2
-rw-r--r--services/java/com/android/server/wm/InputFilter.java256
-rw-r--r--services/java/com/android/server/wm/InputManager.java130
-rw-r--r--services/java/com/android/server/wm/InputMonitor.java4
-rw-r--r--services/java/com/android/server/wm/ViewServer.java2
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java268
-rw-r--r--services/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/jni/Android.mk2
-rw-r--r--services/jni/com_android_server_AlarmManagerService.cpp32
-rw-r--r--services/jni/com_android_server_InputApplication.cpp14
-rw-r--r--services/jni/com_android_server_InputApplicationHandle.cpp10
-rw-r--r--services/jni/com_android_server_InputManager.cpp272
-rw-r--r--services/jni/com_android_server_InputWindow.cpp48
-rw-r--r--services/jni/com_android_server_InputWindowHandle.cpp12
-rw-r--r--services/jni/com_android_server_PowerManagerService.cpp12
-rw-r--r--services/jni/com_android_server_UsbService.cpp31
-rw-r--r--services/sensorservice/Android.mk2
-rw-r--r--services/surfaceflinger/Android.mk2
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.cpp10
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.h2
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp249
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h18
-rw-r--r--services/surfaceflinger/Layer.cpp4
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp72
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h32
-rw-r--r--services/surfaceflinger/TextureManager.cpp2
-rw-r--r--services/surfaceflinger/tests/resize/Android.mk2
-rw-r--r--services/surfaceflinger/tests/resize/resize.cpp7
-rw-r--r--services/surfaceflinger/tests/screencap/Android.mk2
-rw-r--r--services/surfaceflinger/tests/surface/Android.mk2
-rw-r--r--services/surfaceflinger/tests/surface/surface.cpp2
-rw-r--r--services/tests/servicestests/Android.mk4
-rw-r--r--services/tests/servicestests/AndroidManifest.xml6
-rw-r--r--services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java420
-rwxr-xr-xservices/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java105
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java107
124 files changed, 15681 insertions, 11646 deletions
diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp
deleted file mode 100644
index d926cb1..0000000
--- a/services/audioflinger/A2dpAudioInterface.cpp
+++ /dev/null
@@ -1,498 +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.
- */
-
-#include <math.h>
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "A2dpAudioInterface"
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include "A2dpAudioInterface.h"
-#include "audio/liba2dp.h"
-#include <hardware_legacy/power.h>
-
-namespace android {
-
-static const char *sA2dpWakeLock = "A2dpOutputStream";
-#define MAX_WRITE_RETRIES 5
-
-// ----------------------------------------------------------------------------
-
-//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface()
-//{
-// AudioHardwareInterface* hw = 0;
-//
-// hw = AudioHardwareInterface::create();
-// LOGD("new A2dpAudioInterface(hw: %p)", hw);
-// hw = new A2dpAudioInterface(hw);
-// return hw;
-//}
-
-A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) :
- mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false)
-{
-}
-
-A2dpAudioInterface::~A2dpAudioInterface()
-{
- closeOutputStream((AudioStreamOut *)mOutput);
- delete mHardwareInterface;
-}
-
-status_t A2dpAudioInterface::initCheck()
-{
- if (mHardwareInterface == 0) return NO_INIT;
- return mHardwareInterface->initCheck();
-}
-
-AudioStreamOut* A2dpAudioInterface::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
-{
- if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
- LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
- return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
- }
-
- status_t err = 0;
-
- // only one output stream allowed
- if (mOutput) {
- if (status)
- *status = -1;
- return NULL;
- }
-
- // create new output stream
- A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
- if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
- mOutput = out;
- mOutput->setBluetoothEnabled(mBluetoothEnabled);
- mOutput->setSuspended(mSuspended);
- } else {
- delete out;
- }
-
- if (status)
- *status = err;
- return mOutput;
-}
-
-void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) {
- if (mOutput == 0 || mOutput != out) {
- mHardwareInterface->closeOutputStream(out);
- }
- else {
- delete mOutput;
- mOutput = 0;
- }
-}
-
-
-AudioStreamIn* A2dpAudioInterface::openInputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
- AudioSystem::audio_in_acoustics acoustics)
-{
- return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
-}
-
-void A2dpAudioInterface::closeInputStream(AudioStreamIn* in)
-{
- return mHardwareInterface->closeInputStream(in);
-}
-
-status_t A2dpAudioInterface::setMode(int mode)
-{
- return mHardwareInterface->setMode(mode);
-}
-
-status_t A2dpAudioInterface::setMicMute(bool state)
-{
- return mHardwareInterface->setMicMute(state);
-}
-
-status_t A2dpAudioInterface::getMicMute(bool* state)
-{
- return mHardwareInterface->getMicMute(state);
-}
-
-status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 value;
- String8 key;
- status_t status = NO_ERROR;
-
- LOGV("setParameters() %s", keyValuePairs.string());
-
- key = "bluetooth_enabled";
- if (param.get(key, value) == NO_ERROR) {
- mBluetoothEnabled = (value == "true");
- if (mOutput) {
- mOutput->setBluetoothEnabled(mBluetoothEnabled);
- }
- param.remove(key);
- }
- key = String8("A2dpSuspended");
- if (param.get(key, value) == NO_ERROR) {
- mSuspended = (value == "true");
- if (mOutput) {
- mOutput->setSuspended(mSuspended);
- }
- param.remove(key);
- }
-
- if (param.size()) {
- status_t hwStatus = mHardwareInterface->setParameters(param.toString());
- if (status == NO_ERROR) {
- status = hwStatus;
- }
- }
-
- return status;
-}
-
-String8 A2dpAudioInterface::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- AudioParameter a2dpParam = AudioParameter();
- String8 value;
- String8 key;
-
- key = "bluetooth_enabled";
- if (param.get(key, value) == NO_ERROR) {
- value = mBluetoothEnabled ? "true" : "false";
- a2dpParam.add(key, value);
- param.remove(key);
- }
- key = "A2dpSuspended";
- if (param.get(key, value) == NO_ERROR) {
- value = mSuspended ? "true" : "false";
- a2dpParam.add(key, value);
- param.remove(key);
- }
-
- String8 keyValuePairs = a2dpParam.toString();
-
- if (param.size()) {
- if (keyValuePairs != "") {
- keyValuePairs += ";";
- }
- keyValuePairs += mHardwareInterface->getParameters(param.toString());
- }
-
- LOGV("getParameters() %s", keyValuePairs.string());
- return keyValuePairs;
-}
-
-size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
-{
- return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount);
-}
-
-status_t A2dpAudioInterface::setVoiceVolume(float v)
-{
- return mHardwareInterface->setVoiceVolume(v);
-}
-
-status_t A2dpAudioInterface::setMasterVolume(float v)
-{
- return mHardwareInterface->setMasterVolume(v);
-}
-
-status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args)
-{
- return mHardwareInterface->dumpState(fd, args);
-}
-
-// ----------------------------------------------------------------------------
-
-A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
- mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL),
- // assume BT enabled to start, this is safe because its only the
- // enabled->disabled transition we are worried about
- mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false)
-{
- // use any address by default
- strcpy(mA2dpAddress, "00:00:00:00:00:00");
- init();
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
- uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
-{
- int lFormat = pFormat ? *pFormat : 0;
- uint32_t lChannels = pChannels ? *pChannels : 0;
- uint32_t lRate = pRate ? *pRate : 0;
-
- LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate);
-
- // fix up defaults
- if (lFormat == 0) lFormat = format();
- if (lChannels == 0) lChannels = channels();
- if (lRate == 0) lRate = sampleRate();
-
- // check values
- if ((lFormat != format()) ||
- (lChannels != channels()) ||
- (lRate != sampleRate())){
- if (pFormat) *pFormat = format();
- if (pChannels) *pChannels = channels();
- if (pRate) *pRate = sampleRate();
- return BAD_VALUE;
- }
-
- if (pFormat) *pFormat = lFormat;
- if (pChannels) *pChannels = lChannels;
- if (pRate) *pRate = lRate;
-
- mDevice = device;
- mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000;
- return NO_ERROR;
-}
-
-A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut()
-{
- LOGV("A2dpAudioStreamOut destructor");
- close();
- LOGV("A2dpAudioStreamOut destructor returning from close()");
-}
-
-ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
-{
- status_t status = -1;
- {
- Mutex::Autolock lock(mLock);
-
- size_t remaining = bytes;
-
- if (!mBluetoothEnabled || mClosing || mSuspended) {
- LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
- mBluetoothEnabled %d, mClosing %d, mSuspended %d",
- mBluetoothEnabled, mClosing, mSuspended);
- goto Error;
- }
-
- if (mStandby) {
- acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock);
- mStandby = false;
- mLastWriteTime = systemTime();
- }
-
- status = init();
- if (status < 0)
- goto Error;
-
- int retries = MAX_WRITE_RETRIES;
- while (remaining > 0 && retries) {
- status = a2dp_write(mData, buffer, remaining);
- if (status < 0) {
- LOGE("a2dp_write failed err: %d\n", status);
- goto Error;
- }
- if (status == 0) {
- retries--;
- }
- remaining -= status;
- buffer = (char *)buffer + status;
- }
-
- // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread
- // does no spin and starve other threads.
- // NOTE: It is likely that the A2DP headset is being disconnected
- nsecs_t now = systemTime();
- if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) {
- LOGV("A2DP sink runs too fast");
- usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime));
- }
- mLastWriteTime = now;
- return bytes;
-
- }
-Error:
-
- standby();
-
- // Simulate audio output timing in case of error
- usleep(mBufferDurationUs);
-
- return status;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::init()
-{
- if (!mData) {
- status_t status = a2dp_init(44100, 2, &mData);
- if (status < 0) {
- LOGE("a2dp_init failed err: %d\n", status);
- mData = NULL;
- return status;
- }
- a2dp_set_sink(mData, mA2dpAddress);
- }
-
- return 0;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
-{
- Mutex::Autolock lock(mLock);
- return standby_l();
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::standby_l()
-{
- int result = NO_ERROR;
-
- if (!mStandby) {
- LOGV_IF(mClosing || !mBluetoothEnabled, "Standby skip stop: closing %d enabled %d",
- mClosing, mBluetoothEnabled);
- if (!mClosing && mBluetoothEnabled) {
- result = a2dp_stop(mData);
- }
- release_wake_lock(sA2dpWakeLock);
- mStandby = true;
- }
-
- return result;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 value;
- String8 key = String8("a2dp_sink_address");
- status_t status = NO_ERROR;
- int device;
- LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string());
-
- if (param.get(key, value) == NO_ERROR) {
- if (value.length() != strlen("00:00:00:00:00:00")) {
- status = BAD_VALUE;
- } else {
- setAddress(value.string());
- }
- param.remove(key);
- }
- key = String8("closing");
- if (param.get(key, value) == NO_ERROR) {
- mClosing = (value == "true");
- if (mClosing) {
- standby();
- }
- param.remove(key);
- }
- key = AudioParameter::keyRouting;
- if (param.getInt(key, device) == NO_ERROR) {
- if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) {
- mDevice = device;
- status = NO_ERROR;
- } else {
- status = BAD_VALUE;
- }
- param.remove(key);
- }
-
- if (param.size()) {
- status = BAD_VALUE;
- }
- return status;
-}
-
-String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- String8 value;
- String8 key = String8("a2dp_sink_address");
-
- if (param.get(key, value) == NO_ERROR) {
- value = mA2dpAddress;
- param.add(key, value);
- }
- key = AudioParameter::keyRouting;
- if (param.get(key, value) == NO_ERROR) {
- param.addInt(key, (int)mDevice);
- }
-
- LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string());
- return param.toString();
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
-{
- Mutex::Autolock lock(mLock);
-
- if (strlen(address) != strlen("00:00:00:00:00:00"))
- return -EINVAL;
-
- strcpy(mA2dpAddress, address);
- if (mData)
- a2dp_set_sink(mData, mA2dpAddress);
-
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled)
-{
- LOGD("setBluetoothEnabled %d", enabled);
-
- Mutex::Autolock lock(mLock);
-
- mBluetoothEnabled = enabled;
- if (!enabled) {
- return close_l();
- }
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff)
-{
- LOGV("setSuspended %d", onOff);
- mSuspended = onOff;
- standby();
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::close()
-{
- Mutex::Autolock lock(mLock);
- LOGV("A2dpAudioStreamOut::close() calling close_l()");
- return close_l();
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l()
-{
- standby_l();
- if (mData) {
- LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)");
- a2dp_cleanup(mData);
- mData = NULL;
- }
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args)
-{
- return NO_ERROR;
-}
-
-status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames)
-{
- //TODO: enable when supported by driver
- return INVALID_OPERATION;
-}
-
-}; // namespace android
diff --git a/services/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h
deleted file mode 100644
index dbe2c6a..0000000
--- a/services/audioflinger/A2dpAudioInterface.h
+++ /dev/null
@@ -1,138 +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.
- */
-
-#ifndef A2DP_AUDIO_HARDWARE_H
-#define A2DP_AUDIO_HARDWARE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-
-#include <hardware_legacy/AudioHardwareBase.h>
-
-
-namespace android {
-
-class A2dpAudioInterface : public AudioHardwareBase
-{
- class A2dpAudioStreamOut;
-
-public:
- A2dpAudioInterface(AudioHardwareInterface* hw);
- virtual ~A2dpAudioInterface();
- virtual status_t initCheck();
-
- virtual status_t setVoiceVolume(float volume);
- virtual status_t setMasterVolume(float volume);
-
- virtual status_t setMode(int mode);
-
- // mic mute
- virtual status_t setMicMute(bool state);
- virtual status_t getMicMute(bool* state);
-
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
-
- virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
-
- // create I/O streams
- virtual AudioStreamOut* openOutputStream(
- uint32_t devices,
- int *format=0,
- uint32_t *channels=0,
- uint32_t *sampleRate=0,
- status_t *status=0);
- virtual void closeOutputStream(AudioStreamOut* out);
-
- 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);
-// static AudioHardwareInterface* createA2dpInterface();
-
-protected:
- virtual status_t dump(int fd, const Vector<String16>& args);
-
-private:
- class A2dpAudioStreamOut : public AudioStreamOut {
- public:
- A2dpAudioStreamOut();
- virtual ~A2dpAudioStreamOut();
- status_t set(uint32_t device,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate);
- virtual uint32_t sampleRate() const { return 44100; }
- // SBC codec wants a multiple of 512
- virtual size_t bufferSize() const { return 512 * 20; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; }
- virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
- virtual ssize_t write(const void* buffer, size_t bytes);
- status_t standby();
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual status_t getRenderPosition(uint32_t *dspFrames);
-
- private:
- friend class A2dpAudioInterface;
- status_t init();
- status_t close();
- status_t close_l();
- status_t setAddress(const char* address);
- status_t setBluetoothEnabled(bool enabled);
- status_t setSuspended(bool onOff);
- status_t standby_l();
-
- private:
- int mFd;
- bool mStandby;
- int mStartCount;
- int mRetryCount;
- char mA2dpAddress[20];
- void* mData;
- Mutex mLock;
- bool mBluetoothEnabled;
- uint32_t mDevice;
- bool mClosing;
- bool mSuspended;
- nsecs_t mLastWriteTime;
- uint32_t mBufferDurationUs;
- };
-
- friend class A2dpAudioStreamOut;
-
- A2dpAudioStreamOut* mOutput;
- AudioHardwareInterface *mHardwareInterface;
- char mA2dpAddress[20];
- bool mBluetoothEnabled;
- bool mSuspended;
-};
-
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // A2DP_AUDIO_HARDWARE_H
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 69a4adc..2222e8b 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -1,77 +1,5 @@
LOCAL_PATH:= $(call my-dir)
-#AUDIO_POLICY_TEST := true
-#ENABLE_AUDIO_DUMP := true
-
-include $(CLEAR_VARS)
-
-
-ifeq ($(AUDIO_POLICY_TEST),true)
- ENABLE_AUDIO_DUMP := true
-endif
-
-
-LOCAL_SRC_FILES:= \
- AudioHardwareGeneric.cpp \
- AudioHardwareStub.cpp \
- AudioHardwareInterface.cpp
-
-ifeq ($(ENABLE_AUDIO_DUMP),true)
- LOCAL_SRC_FILES += AudioDumpInterface.cpp
- LOCAL_CFLAGS += -DENABLE_AUDIO_DUMP
-endif
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- libbinder \
- libmedia \
- libhardware_legacy
-
-ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
- LOCAL_CFLAGS += -DGENERIC_AUDIO
-endif
-
-LOCAL_MODULE:= libaudiointerface
-
-ifeq ($(BOARD_HAVE_BLUETOOTH),true)
- LOCAL_SRC_FILES += A2dpAudioInterface.cpp
- LOCAL_SHARED_LIBRARIES += liba2dp
- LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
- LOCAL_C_INCLUDES += $(call include-path-for, bluez)
-endif
-
-include $(BUILD_STATIC_LIBRARY)
-
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- AudioPolicyManagerBase.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- libmedia
-
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
-
-LOCAL_MODULE:= libaudiopolicybase
-
-ifeq ($(BOARD_HAVE_BLUETOOTH),true)
- LOCAL_CFLAGS += -DWITH_A2DP
-endif
-
-ifeq ($(AUDIO_POLICY_TEST),true)
- LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
-endif
-
-include $(BUILD_STATIC_LIBRARY)
-
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -87,15 +15,12 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder \
libmedia \
+ libhardware \
libhardware_legacy \
libeffects
-ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
- LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
- LOCAL_CFLAGS += -DGENERIC_AUDIO
-else
- LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy
-endif
+LOCAL_STATIC_LIBRARIES := \
+ libmedia_helper
ifeq ($(TARGET_SIMULATOR),true)
LOCAL_LDLIBS += -ldl
@@ -105,15 +30,6 @@ endif
LOCAL_MODULE:= libaudioflinger
-ifeq ($(BOARD_HAVE_BLUETOOTH),true)
- LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
- LOCAL_SHARED_LIBRARIES += liba2dp
-endif
-
-ifeq ($(AUDIO_POLICY_TEST),true)
- LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
-endif
-
ifeq ($(TARGET_SIMULATOR),true)
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -lrt -lpthread
diff --git a/services/audioflinger/AudioDumpInterface.cpp b/services/audioflinger/AudioDumpInterface.cpp
deleted file mode 100644
index 6c11114..0000000
--- a/services/audioflinger/AudioDumpInterface.cpp
+++ /dev/null
@@ -1,573 +0,0 @@
-/* //device/servers/AudioFlinger/AudioDumpInterface.cpp
-**
-** 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 "AudioFlingerDump"
-//#define LOG_NDEBUG 0
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Log.h>
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "AudioDumpInterface.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw)
- : mPolicyCommands(String8("")), mFileName(String8(""))
-{
- if(hw == 0) {
- LOGE("Dump construct hw = 0");
- }
- mFinalInterface = hw;
- LOGV("Constructor %p, mFinalInterface %p", this, mFinalInterface);
-}
-
-
-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;
-}
-
-
-AudioStreamOut* AudioDumpInterface::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
-{
- AudioStreamOut* outFinal = NULL;
- int lFormat = AudioSystem::PCM_16_BIT;
- uint32_t lChannels = AudioSystem::CHANNEL_OUT_STEREO;
- uint32_t lRate = 44100;
-
-
- outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status);
- if (outFinal != 0) {
- lFormat = outFinal->format();
- lChannels = outFinal->channels();
- lRate = outFinal->sampleRate();
- } else {
- 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("openOutputStream(), outFinal %p", outFinal);
-
- AudioStreamOutDump *dumOutput = new AudioStreamOutDump(this, mOutputs.size(), outFinal,
- devices, lFormat, lChannels, lRate);
- mOutputs.add(dumOutput);
-
- return dumOutput;
-}
-
-void AudioDumpInterface::closeOutputStream(AudioStreamOut* out)
-{
- AudioStreamOutDump *dumpOut = (AudioStreamOutDump *)out;
-
- if (mOutputs.indexOf(dumpOut) < 0) {
- LOGW("Attempt to close invalid output stream");
- return;
- }
-
- LOGV("closeOutputStream() output %p", out);
-
- dumpOut->standby();
- if (dumpOut->finalStream() != NULL) {
- mFinalInterface->closeOutputStream(dumpOut->finalStream());
- }
-
- mOutputs.remove(dumpOut);
- delete dumpOut;
-}
-
-AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format, uint32_t *channels,
- uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics)
-{
- AudioStreamIn* inFinal = NULL;
- int lFormat = AudioSystem::PCM_16_BIT;
- uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO;
- uint32_t lRate = 8000;
-
- 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) {
- 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);
-
- AudioStreamInDump *dumInput = new AudioStreamInDump(this, mInputs.size(), inFinal,
- devices, lFormat, lChannels, lRate);
- mInputs.add(dumInput);
-
- return dumInput;
-}
-void AudioDumpInterface::closeInputStream(AudioStreamIn* in)
-{
- AudioStreamInDump *dumpIn = (AudioStreamInDump *)in;
-
- if (mInputs.indexOf(dumpIn) < 0) {
- LOGW("Attempt to close invalid input stream");
- return;
- }
- dumpIn->standby();
- if (dumpIn->finalStream() != NULL) {
- mFinalInterface->closeInputStream(dumpIn->finalStream());
- }
-
- mInputs.remove(dumpIn);
- delete dumpIn;
-}
-
-
-status_t AudioDumpInterface::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 value;
- int valueInt;
- LOGV("setParameters %s", keyValuePairs.string());
-
- if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) {
- mFileName = value;
- param.remove(String8("test_cmd_file_name"));
- }
- if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) {
- Mutex::Autolock _l(mLock);
- param.remove(String8("test_cmd_policy"));
- mPolicyCommands = param.toString();
- LOGV("test_cmd_policy command %s written", mPolicyCommands.string());
- return NO_ERROR;
- }
-
- if (mFinalInterface != 0 ) return mFinalInterface->setParameters(keyValuePairs);
- return NO_ERROR;
-}
-
-String8 AudioDumpInterface::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- AudioParameter response;
- String8 value;
-
-// LOGV("getParameters %s", keys.string());
- if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) {
- Mutex::Autolock _l(mLock);
- if (mPolicyCommands.length() != 0) {
- response = AudioParameter(mPolicyCommands);
- response.addInt(String8("test_cmd_policy"), 1);
- } else {
- response.addInt(String8("test_cmd_policy"), 0);
- }
- param.remove(String8("test_cmd_policy"));
-// LOGV("test_cmd_policy command %s read", mPolicyCommands.string());
- }
-
- if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) {
- response.add(String8("test_cmd_file_name"), mFileName);
- param.remove(String8("test_cmd_file_name"));
- }
-
- String8 keyValuePairs = response.toString();
-
- if (param.size() && mFinalInterface != 0 ) {
- keyValuePairs += ";";
- keyValuePairs += mFinalInterface->getParameters(param.toString());
- }
-
- 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);
-}
-
-// ----------------------------------------------------------------------------
-
-AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface,
- int id,
- AudioStreamOut* finalStream,
- uint32_t devices,
- int format,
- uint32_t channels,
- uint32_t sampleRate)
- : mInterface(interface), mId(id),
- mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices),
- mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0)
-{
- LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
-}
-
-
-AudioStreamOutDump::~AudioStreamOutDump()
-{
- LOGV("AudioStreamOutDump destructor");
- Close();
-}
-
-ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes)
-{
- ssize_t ret;
-
- if (mFinalStream) {
- ret = mFinalStream->write(buffer, bytes);
- } else {
- usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000);
- ret = bytes;
- }
- if(!mFile) {
- if (mInterface->fileName() != "") {
- char name[255];
- 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 (mFile) {
- fwrite(buffer, bytes, 1, mFile);
- }
- return ret;
-}
-
-status_t AudioStreamOutDump::standby()
-{
- LOGV("AudioStreamOutDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream);
-
- Close();
- if (mFinalStream != 0 ) return mFinalStream->standby();
- return NO_ERROR;
-}
-
-uint32_t AudioStreamOutDump::sampleRate() const
-{
- if (mFinalStream != 0 ) return mFinalStream->sampleRate();
- return mSampleRate;
-}
-
-size_t AudioStreamOutDump::bufferSize() const
-{
- if (mFinalStream != 0 ) return mFinalStream->bufferSize();
- return mBufferSize;
-}
-
-uint32_t AudioStreamOutDump::channels() const
-{
- if (mFinalStream != 0 ) return mFinalStream->channels();
- return mChannels;
-}
-int AudioStreamOutDump::format() const
-{
- if (mFinalStream != 0 ) return mFinalStream->format();
- return mFormat;
-}
-uint32_t AudioStreamOutDump::latency() const
-{
- if (mFinalStream != 0 ) return mFinalStream->latency();
- return 0;
-}
-status_t AudioStreamOutDump::setVolume(float left, float right)
-{
- if (mFinalStream != 0 ) return mFinalStream->setVolume(left, right);
- return NO_ERROR;
-}
-status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs)
-{
- LOGV("AudioStreamOutDump::setParameters %s", keyValuePairs.string());
-
- if (mFinalStream != 0 ) {
- return mFinalStream->setParameters(keyValuePairs);
- }
-
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 value;
- int valueInt;
- status_t status = NO_ERROR;
-
- if (param.getInt(String8("set_id"), valueInt) == NO_ERROR) {
- mId = valueInt;
- }
-
- if (param.getInt(String8("format"), valueInt) == NO_ERROR) {
- if (mFile == 0) {
- mFormat = valueInt;
- } else {
- status = INVALID_OPERATION;
- }
- }
- if (param.getInt(String8("channels"), valueInt) == NO_ERROR) {
- if (valueInt == AudioSystem::CHANNEL_OUT_STEREO || valueInt == AudioSystem::CHANNEL_OUT_MONO) {
- mChannels = valueInt;
- } else {
- status = BAD_VALUE;
- }
- }
- if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) {
- if (valueInt > 0 && valueInt <= 48000) {
- if (mFile == 0) {
- mSampleRate = valueInt;
- } else {
- status = INVALID_OPERATION;
- }
- } else {
- status = BAD_VALUE;
- }
- }
- return status;
-}
-
-String8 AudioStreamOutDump::getParameters(const String8& keys)
-{
- if (mFinalStream != 0 ) return mFinalStream->getParameters(keys);
-
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args)
-{
- if (mFinalStream != 0 ) return mFinalStream->dump(fd, args);
- return NO_ERROR;
-}
-
-void AudioStreamOutDump::Close()
-{
- if(mFile) {
- fclose(mFile);
- mFile = 0;
- }
-}
-
-status_t AudioStreamOutDump::getRenderPosition(uint32_t *dspFrames)
-{
- if (mFinalStream != 0 ) return mFinalStream->getRenderPosition(dspFrames);
- return INVALID_OPERATION;
-}
-
-// ----------------------------------------------------------------------------
-
-AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface,
- int id,
- AudioStreamIn* finalStream,
- uint32_t devices,
- int format,
- uint32_t channels,
- uint32_t sampleRate)
- : mInterface(interface), mId(id),
- mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices),
- mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0)
-{
- LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
-}
-
-
-AudioStreamInDump::~AudioStreamInDump()
-{
- Close();
-}
-
-ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes)
-{
- ssize_t ret;
-
- 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 (mFile) {
- fwrite(buffer, bytes, 1, mFile);
- }
- } 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 (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 ret;
-}
-
-status_t AudioStreamInDump::standby()
-{
- LOGV("AudioStreamInDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream);
-
- Close();
- if (mFinalStream != 0 ) return mFinalStream->standby();
- return NO_ERROR;
-}
-
-status_t AudioStreamInDump::setGain(float gain)
-{
- if (mFinalStream != 0 ) return mFinalStream->setGain(gain);
- return NO_ERROR;
-}
-
-uint32_t AudioStreamInDump::sampleRate() const
-{
- if (mFinalStream != 0 ) return mFinalStream->sampleRate();
- return mSampleRate;
-}
-
-size_t AudioStreamInDump::bufferSize() const
-{
- if (mFinalStream != 0 ) return mFinalStream->bufferSize();
- return mBufferSize;
-}
-
-uint32_t AudioStreamInDump::channels() const
-{
- if (mFinalStream != 0 ) return mFinalStream->channels();
- return mChannels;
-}
-
-int AudioStreamInDump::format() const
-{
- if (mFinalStream != 0 ) return mFinalStream->format();
- return mFormat;
-}
-
-status_t AudioStreamInDump::setParameters(const String8& keyValuePairs)
-{
- LOGV("AudioStreamInDump::setParameters()");
- if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs);
- return NO_ERROR;
-}
-
-String8 AudioStreamInDump::getParameters(const String8& keys)
-{
- if (mFinalStream != 0 ) return mFinalStream->getParameters(keys);
-
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-unsigned int AudioStreamInDump::getInputFramesLost() const
-{
- if (mFinalStream != 0 ) return mFinalStream->getInputFramesLost();
- return 0;
-}
-
-status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args)
-{
- if (mFinalStream != 0 ) return mFinalStream->dump(fd, args);
- return NO_ERROR;
-}
-
-void AudioStreamInDump::Close()
-{
- if(mFile) {
- fclose(mFile);
- mFile = 0;
- }
-}
-}; // namespace android
diff --git a/services/audioflinger/AudioDumpInterface.h b/services/audioflinger/AudioDumpInterface.h
deleted file mode 100644
index 814ce5f..0000000
--- a/services/audioflinger/AudioDumpInterface.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/* //device/servers/AudioFlinger/AudioDumpInterface.h
-**
-** 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.
-*/
-
-#ifndef ANDROID_AUDIO_DUMP_INTERFACE_H
-#define ANDROID_AUDIO_DUMP_INTERFACE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/String8.h>
-#include <utils/SortedVector.h>
-
-#include <hardware_legacy/AudioHardwareBase.h>
-
-namespace android {
-
-#define AUDIO_DUMP_WAVE_HDR_SIZE 44
-
-class AudioDumpInterface;
-
-class AudioStreamOutDump : public AudioStreamOut {
-public:
- AudioStreamOutDump(AudioDumpInterface *interface,
- int id,
- AudioStreamOut* finalStream,
- uint32_t devices,
- int format,
- uint32_t channels,
- uint32_t sampleRate);
- ~AudioStreamOutDump();
-
- virtual ssize_t write(const void* buffer, size_t bytes);
- virtual uint32_t sampleRate() const;
- virtual size_t bufferSize() const;
- virtual uint32_t channels() const;
- virtual int format() const;
- virtual uint32_t latency() const;
- virtual status_t setVolume(float left, float right);
- virtual status_t standby();
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual status_t dump(int fd, const Vector<String16>& args);
- void Close(void);
- AudioStreamOut* finalStream() { return mFinalStream; }
- uint32_t device() { return mDevice; }
- int getId() { return mId; }
- virtual status_t getRenderPosition(uint32_t *dspFrames);
-
-private:
- AudioDumpInterface *mInterface;
- int mId;
- uint32_t mSampleRate; //
- uint32_t mFormat; //
- uint32_t mChannels; // output configuration
- uint32_t mLatency; //
- uint32_t mDevice; // current device this output is routed to
- size_t mBufferSize;
- AudioStreamOut *mFinalStream;
- FILE *mFile; // output file
- int mFileCount;
-};
-
-class AudioStreamInDump : public AudioStreamIn {
-public:
- AudioStreamInDump(AudioDumpInterface *interface,
- int id,
- AudioStreamIn* finalStream,
- uint32_t devices,
- int format,
- uint32_t channels,
- uint32_t sampleRate);
- ~AudioStreamInDump();
-
- virtual uint32_t sampleRate() const;
- virtual size_t bufferSize() const;
- virtual uint32_t channels() const;
- virtual int format() const;
-
- virtual status_t setGain(float gain);
- virtual ssize_t read(void* buffer, ssize_t bytes);
- virtual status_t standby();
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual unsigned int getInputFramesLost() const;
- virtual status_t dump(int fd, const Vector<String16>& args);
- void Close(void);
- AudioStreamIn* finalStream() { return mFinalStream; }
- uint32_t device() { return mDevice; }
-
-private:
- AudioDumpInterface *mInterface;
- int mId;
- uint32_t mSampleRate; //
- uint32_t mFormat; //
- uint32_t mChannels; // output configuration
- uint32_t mDevice; // current device this output is routed to
- size_t mBufferSize;
- AudioStreamIn *mFinalStream;
- FILE *mFile; // output file
- int mFileCount;
-};
-
-class AudioDumpInterface : public AudioHardwareBase
-{
-
-public:
- AudioDumpInterface(AudioHardwareInterface* hw);
- virtual AudioStreamOut* openOutputStream(
- uint32_t devices,
- int *format=0,
- uint32_t *channels=0,
- uint32_t *sampleRate=0,
- status_t *status=0);
- virtual void closeOutputStream(AudioStreamOut* out);
-
- virtual ~AudioDumpInterface();
-
- virtual status_t initCheck()
- {return mFinalInterface->initCheck();}
- virtual status_t setVoiceVolume(float volume)
- {return mFinalInterface->setVoiceVolume(volume);}
- 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);}
- virtual status_t getMicMute(bool* state)
- {return mFinalInterface->getMicMute(state);}
-
- 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);
-
- virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); }
-
- String8 fileName() const { return mFileName; }
-protected:
-
- AudioHardwareInterface *mFinalInterface;
- SortedVector<AudioStreamOutDump *> mOutputs;
- SortedVector<AudioStreamInDump *> mInputs;
- Mutex mLock;
- String8 mPolicyCommands;
- String8 mFileName;
-};
-
-}; // namespace android
-
-#endif // ANDROID_AUDIO_DUMP_INTERFACE_H
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2b08ab5..f8ad5bb 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -31,7 +31,9 @@
#include <binder/IPCThreadState.h>
#include <utils/String16.h>
#include <utils/threads.h>
+#include <utils/Atomic.h>
+#include <cutils/bitops.h>
#include <cutils/properties.h>
#include <media/AudioTrack.h>
@@ -40,15 +42,13 @@
#include <private/media/AudioTrackShared.h>
#include <private/media/AudioEffectShared.h>
-#include <hardware_legacy/AudioHardwareInterface.h>
+
+#include <hardware/audio.h>
+#include <hardware/audio_hal.h>
#include "AudioMixer.h"
#include "AudioFlinger.h"
-#ifdef WITH_A2DP
-#include "A2dpAudioInterface.h"
-#endif
-
#include <media/EffectsFactoryApi.h>
#include <media/EffectVisualizerApi.h>
@@ -136,34 +136,109 @@ static void addBatteryData(uint32_t params) {
service->addBatteryData(params);
}
+static int load_audio_interface(const char *if_name, const hw_module_t **mod,
+ audio_hw_device_t **dev)
+{
+ int rc;
+
+ rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, mod);
+ if (rc)
+ goto out;
+
+ rc = audio_hw_device_open(*mod, dev);
+ LOGE_IF(rc, "couldn't open audio hw device in %s.%s (%s)",
+ AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
+ if (rc)
+ goto out;
+
+ return 0;
+
+out:
+ *mod = NULL;
+ *dev = NULL;
+ return rc;
+}
+
+static const char *audio_interfaces[] = {
+ "primary",
+ "a2dp",
+ "usb",
+};
+#define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0])))
+
// ----------------------------------------------------------------------------
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
+ mPrimaryHardwareDev(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
+{
+}
+
+void AudioFlinger::onFirstRef()
{
+ int rc = 0;
+
Mutex::Autolock _l(mLock);
+ /* TODO: move all this work into an Init() function */
mHardwareStatus = AUDIO_HW_IDLE;
- mAudioHardware = AudioHardwareInterface::create();
+ for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
+ const hw_module_t *mod;
+ audio_hw_device_t *dev;
+
+ rc = load_audio_interface(audio_interfaces[i], &mod, &dev);
+ if (rc)
+ continue;
+
+ LOGI("Loaded %s audio interface from %s (%s)", audio_interfaces[i],
+ mod->name, mod->id);
+ mAudioHwDevs.push(dev);
+
+ if (!mPrimaryHardwareDev) {
+ mPrimaryHardwareDev = dev;
+ LOGI("Using '%s' (%s.%s) as the primary audio interface",
+ mod->name, mod->id, audio_interfaces[i]);
+ }
+ }
mHardwareStatus = AUDIO_HW_INIT;
- if (mAudioHardware->initCheck() == NO_ERROR) {
- AutoMutex lock(mHardwareLock);
- mMode = AudioSystem::MODE_NORMAL;
- mHardwareStatus = AUDIO_HW_SET_MODE;
- mAudioHardware->setMode(mMode);
- mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- mAudioHardware->setMasterVolume(1.0f);
- mHardwareStatus = AUDIO_HW_IDLE;
- } else {
- LOGE("Couldn't even initialize the stubbed audio hardware!");
+
+ if (!mPrimaryHardwareDev || mAudioHwDevs.size() == 0) {
+ LOGE("Primary audio interface not found");
+ return;
}
+
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+
+ mHardwareStatus = AUDIO_HW_INIT;
+ rc = dev->init_check(dev);
+ if (rc == 0) {
+ AutoMutex lock(mHardwareLock);
+
+ mMode = AUDIO_MODE_NORMAL;
+ mHardwareStatus = AUDIO_HW_SET_MODE;
+ dev->set_mode(dev, mMode);
+ mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+ dev->set_master_volume(dev, 1.0f);
+ mHardwareStatus = AUDIO_HW_IDLE;
+ }
+ }
+}
+
+status_t AudioFlinger::initCheck() const
+{
+ Mutex::Autolock _l(mLock);
+ if (mPrimaryHardwareDev == NULL || mAudioHwDevs.size() == 0)
+ return NO_INIT;
+ return NO_ERROR;
}
AudioFlinger::~AudioFlinger()
{
+ int num_devs = mAudioHwDevs.size();
+
while (!mRecordThreads.isEmpty()) {
// closeInput() will remove first entry from mRecordThreads
closeInput(mRecordThreads.keyAt(0));
@@ -172,12 +247,24 @@ AudioFlinger::~AudioFlinger()
// closeOutput() will remove first entry from mPlaybackThreads
closeOutput(mPlaybackThreads.keyAt(0));
}
- if (mAudioHardware) {
- delete mAudioHardware;
+
+ for (int i = 0; i < num_devs; i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ audio_hw_device_close(dev);
}
+ mAudioHwDevs.clear();
}
-
+audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(uint32_t devices)
+{
+ /* first matching HW device is returned */
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ if ((dev->get_supported_devices(dev) & devices) == devices)
+ return dev;
+ }
+ return NULL;
+}
status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
{
@@ -276,8 +363,10 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
mRecordThreads.valueAt(i)->dump(fd, args);
}
- if (mAudioHardware) {
- mAudioHardware->dumpState(fd, args);
+ // dump all hardware devs
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ dev->dump(dev, fd);
}
if (locked) mLock.unlock();
}
@@ -308,7 +397,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
status_t lStatus;
int lSessionId;
- if (streamType >= AudioSystem::NUM_STREAM_TYPES) {
+ if (streamType >= AUDIO_STREAM_CNT) {
LOGE("invalid stream type");
lStatus = BAD_VALUE;
goto Exit;
@@ -334,7 +423,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
}
LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
- if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+ if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
if (mPlaybackThreads.keyAt(i) != output) {
@@ -453,7 +542,7 @@ status_t AudioFlinger::setMasterVolume(float value)
{ // scope for the lock
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- if (mAudioHardware->setMasterVolume(value) == NO_ERROR) {
+ if (mPrimaryHardwareDev->set_master_volume(mPrimaryHardwareDev, value) == NO_ERROR) {
value = 1.0f;
}
mHardwareStatus = AUDIO_HW_IDLE;
@@ -475,7 +564,7 @@ status_t AudioFlinger::setMode(int mode)
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
- if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) {
+ if ((mode < 0) || (mode >= AUDIO_MODE_CNT)) {
LOGW("Illegal value: setMode(%d)", mode);
return BAD_VALUE;
}
@@ -483,7 +572,7 @@ status_t AudioFlinger::setMode(int mode)
{ // scope for the lock
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MODE;
- ret = mAudioHardware->setMode(mode);
+ ret = mPrimaryHardwareDev->set_mode(mPrimaryHardwareDev, mode);
mHardwareStatus = AUDIO_HW_IDLE;
}
@@ -506,16 +595,16 @@ status_t AudioFlinger::setMicMute(bool state)
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
- status_t ret = mAudioHardware->setMicMute(state);
+ status_t ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state);
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
}
bool AudioFlinger::getMicMute() const
{
- bool state = AudioSystem::MODE_INVALID;
+ bool state = AUDIO_MODE_INVALID;
mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
- mAudioHardware->getMicMute(&state);
+ mPrimaryHardwareDev->get_mic_mute(mPrimaryHardwareDev, &state);
mHardwareStatus = AUDIO_HW_IDLE;
return state;
}
@@ -552,7 +641,7 @@ status_t AudioFlinger::setStreamVolume(int stream, float value, int output)
return PERMISSION_DENIED;
}
- if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
@@ -585,8 +674,8 @@ status_t AudioFlinger::setStreamMute(int stream, bool muted)
return PERMISSION_DENIED;
}
- if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES ||
- uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) {
+ if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT ||
+ uint32_t(stream) == AUDIO_STREAM_ENFORCED_AUDIBLE) {
return BAD_VALUE;
}
@@ -600,7 +689,7 @@ status_t AudioFlinger::setStreamMute(int stream, bool muted)
float AudioFlinger::streamVolume(int stream, int output) const
{
- if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) {
return 0.0f;
}
@@ -621,7 +710,7 @@ float AudioFlinger::streamVolume(int stream, int output) const
bool AudioFlinger::streamMute(int stream) const
{
- if (stream < 0 || stream >= (int)AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= (int)AUDIO_STREAM_CNT) {
return true;
}
@@ -643,9 +732,14 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
if (ioHandle == 0) {
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_PARAMETER;
- result = mAudioHardware->setParameters(keyValuePairs);
+ status_t final_result = NO_ERROR;
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ result = dev->set_parameters(dev, keyValuePairs.string());
+ final_result = result ?: final_result;
+ }
mHardwareStatus = AUDIO_HW_IDLE;
- return result;
+ return final_result;
}
// hold a strong ref on thread in case closeOutput() or closeInput() is called
@@ -671,7 +765,15 @@ String8 AudioFlinger::getParameters(int ioHandle, const String8& keys)
// ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid());
if (ioHandle == 0) {
- return mAudioHardware->getParameters(keys);
+ String8 out_s8;
+
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs[i];
+ char *s = dev->get_parameters(dev, keys.string());
+ out_s8 += String8(s);
+ free(s);
+ }
+ return out_s8;
}
Mutex::Autolock _l(mLock);
@@ -689,7 +791,7 @@ String8 AudioFlinger::getParameters(int ioHandle, const String8& keys)
size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
- return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount);
+ return mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, sampleRate, format, channelCount);
}
unsigned int AudioFlinger::getInputFramesLost(int ioHandle)
@@ -716,7 +818,7 @@ status_t AudioFlinger::setVoiceVolume(float value)
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_VOICE_VOLUME;
- status_t ret = mAudioHardware->setVoiceVolume(value);
+ status_t ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value);
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
@@ -967,7 +1069,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
mMasterVolume = mAudioFlinger->masterVolume();
mMasterMute = mAudioFlinger->masterMute();
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
}
@@ -1130,12 +1232,12 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra
// 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);
+ AudioSystem::getStrategyForStream((audio_stream_type_t)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())) {
+ strategy != AudioSystem::getStrategyForStream((audio_stream_type_t)t->type())) {
lStatus = BAD_VALUE;
goto Exit;
}
@@ -1154,7 +1256,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra
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()));
+ chain->setStrategy(AudioSystem::getStrategyForStream((audio_stream_type_t)track->type()));
}
}
lStatus = NO_ERROR;
@@ -1169,7 +1271,7 @@ Exit:
uint32_t AudioFlinger::PlaybackThread::latency() const
{
if (mOutput) {
- return mOutput->latency();
+ return mOutput->stream->get_latency(mOutput->stream);
}
else {
return 0;
@@ -1263,7 +1365,13 @@ void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
{
- return mOutput->getParameters(keys);
+ String8 out_s8;
+ char *s;
+
+ s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string());
+ out_s8 = String8(s);
+ free(s);
+ return out_s8;
}
// destroyTrack_l() must be called with AudioFlinger::mLock held
@@ -1295,12 +1403,12 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
void AudioFlinger::PlaybackThread::readOutputParameters()
{
- mSampleRate = mOutput->sampleRate();
- mChannels = mOutput->channels();
- mChannelCount = (uint16_t)AudioSystem::popCount(mChannels);
- mFormat = mOutput->format();
- mFrameSize = (uint16_t)mOutput->frameSize();
- mFrameCount = mOutput->bufferSize() / mFrameSize;
+ mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
+ mChannels = mOutput->stream->common.get_channels(&mOutput->stream->common);
+ mChannelCount = (uint16_t)popcount(mChannels);
+ mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
+ mFrameSize = (uint16_t)audio_stream_frame_size(&mOutput->stream->common);
+ mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
// FIXME - Current mixer implementation only supports stereo output: Always
// Allocate a stereo buffer even if HW output is mono.
@@ -1328,9 +1436,9 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui
if (mOutput == 0) {
return INVALID_OPERATION;
}
- *halFrames = mBytesWritten/mOutput->frameSize();
+ *halFrames = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- return mOutput->getRenderPosition(dspFrames);
+ return mOutput->stream->get_render_position(mOutput->stream, dspFrames);
}
uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
@@ -1355,19 +1463,19 @@ uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
{
- // session AudioSystem::SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
+ // session AUDIO_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);
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ return AudioSystem::getStrategyForStream(AUDIO_STREAM_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((audio_stream_type_t) track->type());
}
}
- return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+ return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
}
sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId)
@@ -1460,7 +1568,7 @@ bool AudioFlinger::MixerThread::threadLoop()
mSuspended) {
if (!mStandby) {
LOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended);
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
}
@@ -1537,7 +1645,7 @@ bool AudioFlinger::MixerThread::threadLoop()
mInWrite = true;
mBytesWritten += mixBufferSize;
- int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
+ int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
@@ -1572,7 +1680,7 @@ bool AudioFlinger::MixerThread::threadLoop()
}
if (!mStandby) {
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
}
LOGV("MixerThread %p exiting", this);
@@ -1596,7 +1704,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
masterVolume = 0;
}
// Delegate master volume control to effect in output mix effect chain if needed
- sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX);
+ sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
if (chain != 0) {
uint32_t v = (uint32_t)(masterVolume * (1 << 24));
chain->setVolume_l(&v, &v);
@@ -1738,7 +1846,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
tracksToRemove->add(track);
// indicate to client process that the track was disabled because of underrun
- cblk->flags |= CBLK_DISABLED_ON;
+ android_atomic_or(CBLK_DISABLED_ON, &cblk->flags);
} else if (mixerStatus != MIXER_TRACKS_READY) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -1787,10 +1895,8 @@ void AudioFlinger::MixerThread::invalidateTracks(int streamType)
for (size_t i = 0; i < size; i++) {
sp<Track> t = mTracks[i];
if (t->type() == streamType) {
- t->mCblk->lock.lock();
- t->mCblk->flags |= CBLK_INVALID_ON;
+ android_atomic_or(CBLK_INVALID_ON, &t->mCblk->flags);
t->mCblk->cv.signal();
- t->mCblk->lock.unlock();
}
}
}
@@ -1824,14 +1930,14 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- if (value != AudioSystem::PCM_16_BIT) {
+ if (value != AUDIO_FORMAT_PCM_16_BIT) {
status = BAD_VALUE;
} else {
reconfig = true;
}
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- if (value != AudioSystem::CHANNEL_OUT_STEREO) {
+ if (value != AUDIO_CHANNEL_OUT_STEREO) {
status = BAD_VALUE;
} else {
reconfig = true;
@@ -1853,12 +1959,12 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
if (mDevice != value) {
uint32_t params = 0;
// check whether speaker is on
- if (value & AudioSystem::DEVICE_OUT_SPEAKER) {
+ if (value & AUDIO_DEVICE_OUT_SPEAKER) {
params |= IMediaPlayerService::kBatteryDataSpeakerOn;
}
int deviceWithoutSpeaker
- = AudioSystem::DEVICE_OUT_ALL & ~AudioSystem::DEVICE_OUT_SPEAKER;
+ = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
// check if any other device (except speaker) is on
if (value & deviceWithoutSpeaker ) {
params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
@@ -1878,12 +1984,14 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
}
if (status == NO_ERROR) {
- status = mOutput->setParameters(keyValuePair);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
if (!mStandby && status == INVALID_OPERATION) {
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
- status = mOutput->setParameters(keyValuePair);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
}
if (status == NO_ERROR && reconfig) {
delete mAudioMixer;
@@ -1927,7 +2035,7 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>
uint32_t AudioFlinger::MixerThread::activeSleepTimeUs()
{
- return (uint32_t)(mOutput->latency() * 1000) / 2;
+ return (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2;
}
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs()
@@ -1977,12 +2085,12 @@ int32_t mul(int16_t in, int16_t v)
void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp)
{
// Do not apply volume on compressed audio
- if (!AudioSystem::isLinearPCM(mFormat)) {
+ if (!audio_is_linear_pcm(mFormat)) {
return;
}
// convert to signed 16 bit before volume calculation
- if (mFormat == AudioSystem::PCM_8_BIT) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT) {
size_t count = mFrameCount * mChannelCount;
uint8_t *src = (uint8_t *)mMixBuffer + count-1;
int16_t *dst = mMixBuffer + count-1;
@@ -2035,7 +2143,7 @@ void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t ri
}
// convert back to unsigned 8 bit after volume calculation
- if (mFormat == AudioSystem::PCM_8_BIT) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT) {
size_t count = mFrameCount * mChannelCount;
int16_t *src = mMixBuffer;
uint8_t *dst = (uint8_t *)mMixBuffer;
@@ -2091,7 +2199,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
// wait until we have something to do...
if (!mStandby) {
LOGV("Audio hardware entering standby, mixer %p\n", this);
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
}
@@ -2176,7 +2284,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
// If audio HAL implements volume control,
// force software volume to nominal value
- if (mOutput->setVolume(left, right) == NO_ERROR) {
+ if (mOutput->stream->set_volume(mOutput->stream, left, right) == NO_ERROR) {
left = 1.0f;
right = 1.0f;
}
@@ -2277,7 +2385,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
} else {
sleepTime = idleSleepTime;
}
- } else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) {
+ } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
memset (mMixBuffer, 0, mFrameCount * mFrameSize);
sleepTime = 0;
}
@@ -2299,7 +2407,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
mLastWriteTime = systemTime();
mInWrite = true;
mBytesWritten += mixBufferSize;
- int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
+ int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
mNumWrites++;
mInWrite = false;
@@ -2321,7 +2429,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
}
if (!mStandby) {
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
}
LOGV("DirectOutputThread %p exiting", this);
@@ -2361,12 +2469,14 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
}
}
if (status == NO_ERROR) {
- status = mOutput->setParameters(keyValuePair);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
if (!mStandby && status == INVALID_OPERATION) {
- mOutput->standby();
+ mOutput->stream->common.standby(&mOutput->stream->common);
mStandby = true;
mBytesWritten = 0;
- status = mOutput->setParameters(keyValuePair);
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
}
if (status == NO_ERROR && reconfig) {
readOutputParameters();
@@ -2386,8 +2496,8 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs()
{
uint32_t time;
- if (AudioSystem::isLinearPCM(mFormat)) {
- time = (uint32_t)(mOutput->latency() * 1000) / 2;
+ if (audio_is_linear_pcm(mFormat)) {
+ time = (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2;
} else {
time = 10000;
}
@@ -2397,7 +2507,7 @@ uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs()
uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs()
{
uint32_t time;
- if (AudioSystem::isLinearPCM(mFormat)) {
+ if (audio_is_linear_pcm(mFormat)) {
time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
} else {
time = 10000;
@@ -2408,7 +2518,7 @@ uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs()
uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
{
uint32_t time;
- if (AudioSystem::isLinearPCM(mFormat)) {
+ if (audio_is_linear_pcm(mFormat)) {
time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
} else {
time = 10000;
@@ -2589,7 +2699,7 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
mChannelCount,
frameCount);
if (outputTrack->cblk() != NULL) {
- thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f);
+ thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
mOutputTracks.add(outputTrack);
LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
updateWaitTime();
@@ -2834,7 +2944,7 @@ AudioFlinger::PlaybackThread::Track::Track(
mStreamType = streamType;
// NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
// 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
- mCblk->frameSize = AudioSystem::isLinearPCM(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t);
+ mCblk->frameSize = audio_is_linear_pcm(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t);
}
}
@@ -2865,7 +2975,7 @@ void AudioFlinger::PlaybackThread::Track::destroy()
if (!isOutputTrack()) {
if (mState == ACTIVE || mState == RESUMING) {
AudioSystem::stopOutput(thread->id(),
- (AudioSystem::stream_type)mStreamType,
+ (audio_stream_type_t)mStreamType,
mSessionId);
// to track the speaker usage
@@ -2949,7 +3059,7 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const {
if (mCblk->framesReady() >= mCblk->frameCount ||
(mCblk->flags & CBLK_FORCEREADY_MSK)) {
mFillingUpStatus = FS_FILLED;
- mCblk->flags &= ~CBLK_FORCEREADY_MSK;
+ android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
return true;
}
return false;
@@ -2977,7 +3087,7 @@ status_t AudioFlinger::PlaybackThread::Track::start()
if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
thread->mLock.unlock();
status = AudioSystem::startOutput(thread->id(),
- (AudioSystem::stream_type)mStreamType,
+ (audio_stream_type_t)mStreamType,
mSessionId);
thread->mLock.lock();
@@ -3017,7 +3127,7 @@ void AudioFlinger::PlaybackThread::Track::stop()
if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
thread->mLock.unlock();
AudioSystem::stopOutput(thread->id(),
- (AudioSystem::stream_type)mStreamType,
+ (audio_stream_type_t)mStreamType,
mSessionId);
thread->mLock.lock();
@@ -3039,7 +3149,7 @@ void AudioFlinger::PlaybackThread::Track::pause()
if (!isOutputTrack()) {
thread->mLock.unlock();
AudioSystem::stopOutput(thread->id(),
- (AudioSystem::stream_type)mStreamType,
+ (audio_stream_type_t)mStreamType,
mSessionId);
thread->mLock.lock();
@@ -3063,14 +3173,12 @@ void AudioFlinger::PlaybackThread::Track::flush()
// STOPPED state
mState = STOPPED;
- mCblk->lock.lock();
- // NOTE: reset() will reset cblk->user and cblk->server with
- // the risk that at the same time, the AudioMixer is trying to read
- // data. In this case, getNextBuffer() would return a NULL pointer
- // as audio buffer => the AudioMixer code MUST always test that pointer
- // returned by getNextBuffer() is not NULL!
- reset();
- mCblk->lock.unlock();
+ // do not reset the track if it is still in the process of being stopped or paused.
+ // this will be done by prepareTracks_l() when the track is stopped.
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ reset();
+ }
}
}
@@ -3082,8 +3190,8 @@ void AudioFlinger::PlaybackThread::Track::reset()
TrackBase::reset();
// Force underrun condition to avoid false underrun callback until first data is
// written to buffer
- mCblk->flags |= CBLK_UNDERRUN_ON;
- mCblk->flags &= ~CBLK_FORCEREADY_MSK;
+ android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
+ android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
mFillingUpStatus = FS_FILLING;
mResetDone = true;
}
@@ -3135,9 +3243,9 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
{
if (mCblk != NULL) {
LOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
- if (format == AudioSystem::PCM_16_BIT) {
+ if (format == AUDIO_FORMAT_PCM_16_BIT) {
mCblk->frameSize = channelCount * sizeof(int16_t);
- } else if (format == AudioSystem::PCM_8_BIT) {
+ } else if (format == AUDIO_FORMAT_PCM_8_BIT) {
mCblk->frameSize = channelCount * sizeof(int8_t);
} else {
mCblk->frameSize = sizeof(int8_t);
@@ -3212,7 +3320,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop()
TrackBase::reset();
// Force overerrun condition to avoid false overrun callback until first data is
// read from buffer
- mCblk->flags |= CBLK_UNDERRUN_ON;
+ android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
}
}
@@ -3240,7 +3348,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
int format,
int channelCount,
int frameCount)
- : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL, 0),
+ : Track(thread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelCount, frameCount, NULL, 0),
mActive(false), mSourceThread(sourceThread)
{
@@ -3612,7 +3720,7 @@ sp<IAudioRecord> AudioFlinger::openRecord(
}
// If no audio session id is provided, create one here
- if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+ if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
lSessionId = *sessionId;
} else {
lSessionId = nextUniqueId_l();
@@ -3682,7 +3790,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, A
ThreadBase(audioFlinger, id),
mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0)
{
- mReqChannelCount = AudioSystem::popCount(channels);
+ mReqChannelCount = popcount(channels);
mReqSampleRate = sampleRate;
readInputParameters();
}
@@ -3724,7 +3832,7 @@ bool AudioFlinger::RecordThread::threadLoop()
checkForNewParameters_l();
if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
if (!mStandby) {
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
mStandby = true;
}
@@ -3739,7 +3847,7 @@ bool AudioFlinger::RecordThread::threadLoop()
if (mActiveTrack != 0) {
if (mActiveTrack->mState == TrackBase::PAUSING) {
if (!mStandby) {
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
mStandby = true;
}
mActiveTrack.clear();
@@ -3784,7 +3892,7 @@ bool AudioFlinger::RecordThread::threadLoop()
mRsmpInIndex += framesIn;
framesOut -= framesIn;
if ((int)mChannelCount == mReqChannelCount ||
- mFormat != AudioSystem::PCM_16_BIT) {
+ mFormat != AUDIO_FORMAT_PCM_16_BIT) {
memcpy(dst, src, framesIn * mFrameSize);
} else {
int16_t *src16 = (int16_t *)src;
@@ -3804,11 +3912,11 @@ bool AudioFlinger::RecordThread::threadLoop()
}
if (framesOut && mFrameCount == mRsmpInIndex) {
if (framesOut == mFrameCount &&
- ((int)mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) {
- mBytesRead = mInput->read(buffer.raw, mInputBytes);
+ ((int)mChannelCount == mReqChannelCount || mFormat != AUDIO_FORMAT_PCM_16_BIT)) {
+ mBytesRead = mInput->stream->read(mInput->stream, buffer.raw, mInputBytes);
framesOut = 0;
} else {
- mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
mRsmpInIndex = 0;
}
if (mBytesRead < 0) {
@@ -3816,7 +3924,7 @@ bool AudioFlinger::RecordThread::threadLoop()
if (mActiveTrack->mState == TrackBase::ACTIVE) {
// Force input into standby so that it tries to
// recover at next read attempt
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
usleep(5000);
}
mRsmpInIndex = mFrameCount;
@@ -3871,7 +3979,7 @@ bool AudioFlinger::RecordThread::threadLoop()
}
if (!mStandby) {
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
}
mActiveTrack.clear();
@@ -4003,13 +4111,13 @@ status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer*
int channelCount;
if (framesReady == 0) {
- mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
if (mBytesRead < 0) {
LOGE("RecordThread::getNextBuffer() Error reading audio input");
if (mActiveTrack->mState == TrackBase::ACTIVE) {
// Force input into standby so that it tries to
// recover at next read attempt
- mInput->standby();
+ mInput->stream->common.standby(&mInput->stream->common);
usleep(5000);
}
buffer->raw = 0;
@@ -4062,7 +4170,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- reqChannelCount = AudioSystem::popCount(value);
+ reqChannelCount = popcount(value);
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
@@ -4076,16 +4184,18 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
}
}
if (status == NO_ERROR) {
- status = mInput->setParameters(keyValuePair);
+ status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string());
if (status == INVALID_OPERATION) {
- mInput->standby();
- status = mInput->setParameters(keyValuePair);
+ mInput->stream->common.standby(&mInput->stream->common);
+ status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string());
}
if (reconfig) {
if (status == BAD_VALUE &&
- reqFormat == mInput->format() && reqFormat == AudioSystem::PCM_16_BIT &&
- ((int)mInput->sampleRate() <= 2 * reqSamplingRate) &&
- (AudioSystem::popCount(mInput->channels()) < 3) && (reqChannelCount < 3)) {
+ reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
+ reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
+ ((int)mInput->stream->common.get_sample_rate(&mInput->stream->common) <= (2 * reqSamplingRate)) &&
+ (popcount(mInput->stream->common.get_channels(&mInput->stream->common)) < 3) &&
+ (reqChannelCount < 3)) {
status = NO_ERROR;
}
if (status == NO_ERROR) {
@@ -4106,7 +4216,13 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
{
- return mInput->getParameters(keys);
+ char *s;
+ String8 out_s8;
+
+ s = mInput->stream->common.get_parameters(&mInput->stream->common, keys.string());
+ out_s8 = String8(s);
+ free(s);
+ return out_s8;
}
void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
@@ -4138,12 +4254,12 @@ void AudioFlinger::RecordThread::readInputParameters()
if (mResampler) delete mResampler;
mResampler = 0;
- mSampleRate = mInput->sampleRate();
- mChannels = mInput->channels();
- mChannelCount = (uint16_t)AudioSystem::popCount(mChannels);
- mFormat = mInput->format();
- mFrameSize = (uint16_t)mInput->frameSize();
- mInputBytes = mInput->bufferSize();
+ mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
+ mChannels = mInput->stream->common.get_channels(&mInput->stream->common);
+ mChannelCount = (uint16_t)popcount(mChannels);
+ mFormat = mInput->stream->common.get_format(&mInput->stream->common);
+ mFrameSize = (uint16_t)audio_stream_frame_size(&mInput->stream->common);
+ mInputBytes = mInput->stream->common.get_buffer_size(&mInput->stream->common);
mFrameCount = mInputBytes / mFrameSize;
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
@@ -4173,7 +4289,7 @@ void AudioFlinger::RecordThread::readInputParameters()
unsigned int AudioFlinger::RecordThread::getInputFramesLost()
{
- return mInput->getInputFramesLost();
+ return mInput->stream->get_input_frames_lost(mInput->stream);
}
// ----------------------------------------------------------------------------
@@ -4192,6 +4308,8 @@ int AudioFlinger::openOutput(uint32_t *pDevices,
uint32_t format = pFormat ? *pFormat : 0;
uint32_t channels = pChannels ? *pChannels : 0;
uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
+ audio_stream_out_t *outStream;
+ audio_hw_device_t *outHwDev;
LOGV("openOutput(), Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
pDevices ? *pDevices : 0,
@@ -4203,26 +4321,30 @@ int AudioFlinger::openOutput(uint32_t *pDevices,
if (pDevices == NULL || *pDevices == 0) {
return 0;
}
+
Mutex::Autolock _l(mLock);
- AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices,
- (int *)&format,
- &channels,
- &samplingRate,
- &status);
+ outHwDev = findSuitableHwDev_l(*pDevices);
+ if (outHwDev == NULL)
+ return 0;
+
+ status = outHwDev->open_output_stream(outHwDev, *pDevices, (int *)&format,
+ &channels, &samplingRate, &outStream);
LOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d",
- output,
+ outStream,
samplingRate,
format,
channels,
status);
mHardwareStatus = AUDIO_HW_IDLE;
- if (output != 0) {
+ if (outStream != NULL) {
+ AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);
int id = nextUniqueId_l();
- if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
- (format != AudioSystem::PCM_16_BIT) ||
- (channels != AudioSystem::CHANNEL_OUT_STEREO)) {
+
+ if ((flags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT) ||
+ (format != AUDIO_FORMAT_PCM_16_BIT) ||
+ (channels != AUDIO_CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this, output, id, *pDevices);
LOGV("openOutput() created direct output: ID %d thread %p", id, thread);
} else {
@@ -4293,7 +4415,9 @@ status_t AudioFlinger::closeOutput(int output)
thread->exit();
if (thread->type() != PlaybackThread::DUPLICATING) {
- mAudioHardware->closeOutputStream(thread->getOutput());
+ AudioStreamOut *out = thread->getOutput();
+ out->hwDev->close_output_stream(out->hwDev, out->stream);
+ delete out;
}
return NO_ERROR;
}
@@ -4343,20 +4467,25 @@ int AudioFlinger::openInput(uint32_t *pDevices,
uint32_t reqSamplingRate = samplingRate;
uint32_t reqFormat = format;
uint32_t reqChannels = channels;
+ audio_stream_in_t *inStream;
+ audio_hw_device_t *inHwDev;
if (pDevices == NULL || *pDevices == 0) {
return 0;
}
+
Mutex::Autolock _l(mLock);
- AudioStreamIn *input = mAudioHardware->openInputStream(*pDevices,
- (int *)&format,
- &channels,
- &samplingRate,
- &status,
- (AudioSystem::audio_in_acoustics)acoustics);
+ inHwDev = findSuitableHwDev_l(*pDevices);
+ if (inHwDev == NULL)
+ return 0;
+
+ status = inHwDev->open_input_stream(inHwDev, *pDevices, (int *)&format,
+ &channels, &samplingRate,
+ (audio_in_acoustics_t)acoustics,
+ &inStream);
LOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d",
- input,
+ inStream,
samplingRate,
format,
channels,
@@ -4366,20 +4495,20 @@ int AudioFlinger::openInput(uint32_t *pDevices,
// If the input could not be opened with the requested parameters and we can handle the conversion internally,
// try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo
// or stereo to mono conversions on 16 bit PCM inputs.
- if (input == 0 && status == BAD_VALUE &&
- reqFormat == format && format == AudioSystem::PCM_16_BIT &&
+ if (inStream == NULL && status == BAD_VALUE &&
+ reqFormat == format && format == AUDIO_FORMAT_PCM_16_BIT &&
(samplingRate <= 2 * reqSamplingRate) &&
- (AudioSystem::popCount(channels) < 3) && (AudioSystem::popCount(reqChannels) < 3)) {
+ (popcount(channels) < 3) && (popcount(reqChannels) < 3)) {
LOGV("openInput() reopening with proposed sampling rate and channels");
- input = mAudioHardware->openInputStream(*pDevices,
- (int *)&format,
- &channels,
- &samplingRate,
- &status,
- (AudioSystem::audio_in_acoustics)acoustics);
+ status = inHwDev->open_input_stream(inHwDev, *pDevices, (int *)&format,
+ &channels, &samplingRate,
+ (audio_in_acoustics_t)acoustics,
+ &inStream);
}
- if (input != 0) {
+ if (inStream != NULL) {
+ AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
+
int id = nextUniqueId_l();
// Start record thread
thread = new RecordThread(this, input, reqSamplingRate, reqChannels, id);
@@ -4389,7 +4518,7 @@ int AudioFlinger::openInput(uint32_t *pDevices,
if (pFormat) *pFormat = format;
if (pChannels) *pChannels = reqChannels;
- input->standby();
+ input->stream->common.standby(&input->stream->common);
// notify client processes of the new input creation
thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
@@ -4418,7 +4547,9 @@ status_t AudioFlinger::closeInput(int input)
}
thread->exit();
- mAudioHardware->closeInputStream(thread->getInput());
+ AudioStreamIn *in = thread->getInput();
+ in->hwDev->close_input_stream(in->hwDev, in->stream);
+ delete in;
return NO_ERROR;
}
@@ -4572,14 +4703,14 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
}
// check audio settings permission for global effects
- if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX && !settingsAllowed()) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
- // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
+ // Session AUDIO_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() != pid) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && getpid() != pid) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
@@ -4593,12 +4724,12 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
}
if (output == 0) {
- if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
// output must be specified by AudioPolicyManager when using session
- // AudioSystem::SESSION_OUTPUT_STAGE
+ // AUDIO_SESSION_OUTPUT_STAGE
lStatus = BAD_VALUE;
goto Exit;
- } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+ } else if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
// if the output returned by getOutputForEffect() is removed before we lock the
// mutex below, the call to checkPlaybackThread_l(output) below will detect it
// and we will exit safely
@@ -4646,7 +4777,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
// an auxiliary version of this effect type is available
found = true;
memcpy(&d, &desc, sizeof(effect_descriptor_t));
- if (sessionId != AudioSystem::SESSION_OUTPUT_MIX ||
+ if (sessionId != AUDIO_SESSION_OUTPUT_MIX ||
(desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
break;
}
@@ -4659,14 +4790,14 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
}
// 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 &&
+ if (sessionId == AUDIO_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 &&
+ if (sessionId != AUDIO_SESSION_OUTPUT_MIX &&
(desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
lStatus = INVALID_OPERATION;
goto Exit;
@@ -4839,7 +4970,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l(
// 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) {
+ sessionId != AUDIO_SESSION_OUTPUT_MIX) {
LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
desc->name, sessionId);
lStatus = BAD_VALUE;
@@ -4848,7 +4979,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l(
// 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) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX && mType != MIXER) {
LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
desc->name, sessionId);
lStatus = BAD_VALUE;
@@ -5035,13 +5166,13 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& c
chain->setInBuffer(buffer, ownsBuffer);
chain->setOutBuffer(mMixBuffer);
- // Effect chain for session AudioSystem::SESSION_OUTPUT_STAGE is inserted at end of effect
+ // Effect chain for session AUDIO_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
+ // Effect chain for session AUDIO_SESSION_OUTPUT_MIX is inserted before
+ // session AUDIO_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
+ // It is therefore mandatory that AUDIO_SESSION_OUTPUT_MIX == 0 and
+ // that AUDIO_SESSION_OUTPUT_STAGE < AUDIO_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
@@ -5121,8 +5252,8 @@ status_t AudioFlinger::PlaybackThread::attachAuxEffect_l(
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);
+ // Auxiliary effects are always in audio session AUDIO_SESSION_OUTPUT_MIX
+ sp<EffectModule> effect = getEffect_l(AUDIO_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());
@@ -5406,7 +5537,7 @@ status_t AudioFlinger::EffectModule::configure()
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,
+ // - in session AUDIO_SESSION_OUTPUT_MIX or AUDIO_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
@@ -5687,17 +5818,17 @@ status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
// 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
+ DEVICE_EARPIECE, // AUDIO_DEVICE_OUT_EARPIECE
+ DEVICE_SPEAKER, // AUDIO_DEVICE_OUT_SPEAKER
+ DEVICE_WIRED_HEADSET, // case AUDIO_DEVICE_OUT_WIRED_HEADSET
+ DEVICE_WIRED_HEADPHONE, // AUDIO_DEVICE_OUT_WIRED_HEADPHONE
+ DEVICE_BLUETOOTH_SCO, // AUDIO_DEVICE_OUT_BLUETOOTH_SCO
+ DEVICE_BLUETOOTH_SCO_HEADSET, // AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET
+ DEVICE_BLUETOOTH_SCO_CARKIT, // AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+ DEVICE_BLUETOOTH_A2DP, // AUDIO_DEVICE_OUT_BLUETOOTH_A2DP
+ DEVICE_BLUETOOTH_A2DP_HEADPHONES, // AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+ DEVICE_BLUETOOTH_A2DP_SPEAKER, // AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
+ DEVICE_AUX_DIGITAL // AUDIO_DEVICE_OUT_AUX_DIGITAL
};
uint32_t AudioFlinger::EffectModule::deviceAudioSystemToEffectApi(uint32_t device)
@@ -5707,7 +5838,7 @@ uint32_t AudioFlinger::EffectModule::deviceAudioSystemToEffectApi(uint32_t devic
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);
+ LOGE("device conversion error for AudioSystem device 0x%08x", device);
return 0;
}
deviceOut |= (uint32_t)sDeviceConvTable[i];
@@ -5717,10 +5848,10 @@ uint32_t AudioFlinger::EffectModule::deviceAudioSystemToEffectApi(uint32_t devic
// 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
- AUDIO_MODE_IN_CALL // AudioSystem::MODE_IN_COMMUNICATION, same conversion as for MODE_IN_CALL
+ AUDIO_EFFECT_MODE_NORMAL, // AUDIO_MODE_NORMAL
+ AUDIO_EFFECT_MODE_RINGTONE, // AUDIO_MODE_RINGTONE
+ AUDIO_EFFECT_MODE_IN_CALL, // AUDIO_MODE_IN_CALL
+ AUDIO_EFFECT_MODE_IN_CALL // AUDIO_MODE_IN_COMMUNICATION, same conversion as for AUDIO_MODE_IN_CALL
};
int AudioFlinger::EffectModule::modeAudioSystemToEffectApi(uint32_t mode)
@@ -6030,7 +6161,7 @@ AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
{
- mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+ mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
}
AudioFlinger::EffectChain::~EffectChain()
@@ -6081,8 +6212,8 @@ void AudioFlinger::EffectChain::process_l()
return;
}
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- bool isGlobalSession = (mSessionId == AudioSystem::SESSION_OUTPUT_MIX) ||
- (mSessionId == AudioSystem::SESSION_OUTPUT_STAGE);
+ bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) ||
+ (mSessionId == AUDIO_SESSION_OUTPUT_STAGE);
bool tracksOnSession = false;
if (!isGlobalSession) {
tracksOnSession =
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index ec3d202..22e5116 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -32,12 +32,14 @@
#include <utils/Errors.h>
#include <utils/threads.h>
#include <utils/SortedVector.h>
+#include <utils/TypeHelpers.h>
#include <utils/Vector.h>
#include <binder/BinderService.h>
#include <binder/MemoryDealer.h>
-#include <hardware_legacy/AudioHardwareInterface.h>
+#include <hardware/audio.h>
+#include <hardware/audio_hal.h>
#include "AudioBufferProvider.h"
@@ -49,7 +51,6 @@ class AudioMixer;
class AudioBuffer;
class AudioResampler;
-
// ----------------------------------------------------------------------------
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
@@ -211,6 +212,9 @@ private:
AudioFlinger();
virtual ~AudioFlinger();
+ status_t initCheck() const;
+ virtual void onFirstRef();
+ audio_hw_device_t* findSuitableHwDev_l(uint32_t devices);
// Internal dump utilites.
status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
@@ -268,6 +272,8 @@ private:
class EffectModule;
class EffectHandle;
class EffectChain;
+ struct AudioStreamOut;
+ struct AudioStreamIn;
class ThreadBase : public Thread {
public:
@@ -495,7 +501,7 @@ private:
void reset();
bool isOutputTrack() const {
- return (mStreamType == AudioSystem::NUM_STREAM_TYPES);
+ return (mStreamType == AUDIO_STREAM_CNT);
}
// we don't really need a lock for these
@@ -689,7 +695,7 @@ private:
SortedVector< sp<Track> > mTracks;
// mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
- stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES + 1];
+ stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1];
AudioStreamOut* mOutput;
float mMasterVolume;
nsecs_t mLastWriteTime;
@@ -1159,21 +1165,37 @@ private:
uint32_t mStrategy; // strategy for this effect chain
};
+ struct AudioStreamOut {
+ audio_hw_device_t *hwDev;
+ audio_stream_out_t *stream;
+
+ AudioStreamOut(audio_hw_device_t *dev, audio_stream_out_t *out) :
+ hwDev(dev), stream(out) {}
+ };
+
+ struct AudioStreamIn {
+ audio_hw_device_t *hwDev;
+ audio_stream_in_t *stream;
+
+ AudioStreamIn(audio_hw_device_t *dev, audio_stream_in_t *in) :
+ hwDev(dev), stream(in) {}
+ };
+
friend class RecordThread;
friend class PlaybackThread;
-
mutable Mutex mLock;
DefaultKeyedVector< pid_t, wp<Client> > mClients;
mutable Mutex mHardwareLock;
- AudioHardwareInterface* mAudioHardware;
+ audio_hw_device_t* mPrimaryHardwareDev;
+ Vector<audio_hw_device_t*> mAudioHwDevs;
mutable int mHardwareStatus;
DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads;
- PlaybackThread::stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES];
+ PlaybackThread::stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
float mMasterVolume;
bool mMasterMute;
@@ -1185,6 +1207,7 @@ private:
};
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/services/audioflinger/AudioHardwareGeneric.cpp b/services/audioflinger/AudioHardwareGeneric.cpp
deleted file mode 100644
index d63c031..0000000
--- a/services/audioflinger/AudioHardwareGeneric.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
-**
-** Copyright 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 <stdint.h>
-#include <sys/types.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sched.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#define LOG_TAG "AudioHardware"
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include "AudioHardwareGeneric.h"
-#include <media/AudioRecord.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static char const * const kAudioDeviceName = "/dev/eac";
-
-// ----------------------------------------------------------------------------
-
-AudioHardwareGeneric::AudioHardwareGeneric()
- : mOutput(0), mInput(0), mFd(-1), mMicMute(false)
-{
- mFd = ::open(kAudioDeviceName, O_RDWR);
-}
-
-AudioHardwareGeneric::~AudioHardwareGeneric()
-{
- if (mFd >= 0) ::close(mFd);
- closeOutputStream((AudioStreamOut *)mOutput);
- closeInputStream((AudioStreamIn *)mInput);
-}
-
-status_t AudioHardwareGeneric::initCheck()
-{
- if (mFd >= 0) {
- if (::access(kAudioDeviceName, O_RDWR) == NO_ERROR)
- return NO_ERROR;
- }
- return NO_INIT;
-}
-
-AudioStreamOut* AudioHardwareGeneric::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
-{
- AutoMutex lock(mLock);
-
- // only one output stream allowed
- if (mOutput) {
- if (status) {
- *status = INVALID_OPERATION;
- }
- return 0;
- }
-
- // create new output stream
- AudioStreamOutGeneric* out = new AudioStreamOutGeneric();
- status_t lStatus = out->set(this, mFd, devices, format, channels, sampleRate);
- if (status) {
- *status = lStatus;
- }
- if (lStatus == NO_ERROR) {
- mOutput = out;
- } else {
- delete out;
- }
- return mOutput;
-}
-
-void AudioHardwareGeneric::closeOutputStream(AudioStreamOut* out) {
- if (mOutput && out == mOutput) {
- delete mOutput;
- mOutput = 0;
- }
-}
-
-AudioStreamIn* AudioHardwareGeneric::openInputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate,
- status_t *status, AudioSystem::audio_in_acoustics acoustics)
-{
- // check for valid input source
- if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
- return 0;
- }
-
- AutoMutex lock(mLock);
-
- // only one input stream allowed
- if (mInput) {
- if (status) {
- *status = INVALID_OPERATION;
- }
- return 0;
- }
-
- // create new output stream
- AudioStreamInGeneric* in = new AudioStreamInGeneric();
- status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics);
- if (status) {
- *status = lStatus;
- }
- if (lStatus == NO_ERROR) {
- mInput = in;
- } else {
- delete in;
- }
- return mInput;
-}
-
-void AudioHardwareGeneric::closeInputStream(AudioStreamIn* in) {
- if (mInput && in == mInput) {
- delete mInput;
- mInput = 0;
- }
-}
-
-status_t AudioHardwareGeneric::setVoiceVolume(float v)
-{
- // Implement: set voice volume
- return NO_ERROR;
-}
-
-status_t AudioHardwareGeneric::setMasterVolume(float v)
-{
- // Implement: set master volume
- // return error - software mixer will handle it
- return INVALID_OPERATION;
-}
-
-status_t AudioHardwareGeneric::setMicMute(bool state)
-{
- mMicMute = state;
- return NO_ERROR;
-}
-
-status_t AudioHardwareGeneric::getMicMute(bool* state)
-{
- *state = mMicMute;
- return NO_ERROR;
-}
-
-status_t AudioHardwareGeneric::dumpInternals(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- result.append("AudioHardwareGeneric::dumpInternals\n");
- snprintf(buffer, SIZE, "\tmFd: %d mMicMute: %s\n", mFd, mMicMute? "true": "false");
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args)
-{
- dumpInternals(fd, args);
- if (mInput) {
- mInput->dump(fd, args);
- }
- if (mOutput) {
- mOutput->dump(fd, args);
- }
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t AudioStreamOutGeneric::set(
- AudioHardwareGeneric *hw,
- int fd,
- uint32_t devices,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate)
-{
- int lFormat = pFormat ? *pFormat : 0;
- uint32_t lChannels = pChannels ? *pChannels : 0;
- uint32_t lRate = pRate ? *pRate : 0;
-
- // fix up defaults
- if (lFormat == 0) lFormat = format();
- if (lChannels == 0) lChannels = channels();
- if (lRate == 0) lRate = sampleRate();
-
- // check values
- if ((lFormat != format()) ||
- (lChannels != channels()) ||
- (lRate != sampleRate())) {
- if (pFormat) *pFormat = format();
- if (pChannels) *pChannels = channels();
- if (pRate) *pRate = sampleRate();
- return BAD_VALUE;
- }
-
- if (pFormat) *pFormat = lFormat;
- if (pChannels) *pChannels = lChannels;
- if (pRate) *pRate = lRate;
-
- mAudioHardware = hw;
- mFd = fd;
- mDevice = devices;
- return NO_ERROR;
-}
-
-AudioStreamOutGeneric::~AudioStreamOutGeneric()
-{
-}
-
-ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes)
-{
- Mutex::Autolock _l(mLock);
- return ssize_t(::write(mFd, buffer, bytes));
-}
-
-status_t AudioStreamOutGeneric::standby()
-{
- // Implement: audio hardware to standby mode
- return NO_ERROR;
-}
-
-status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioStreamOutGeneric::dump\n");
- result.append(buffer);
- snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tformat: %d\n", format());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice);
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware);
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioStreamOutGeneric::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 key = String8(AudioParameter::keyRouting);
- status_t status = NO_ERROR;
- int device;
- LOGV("setParameters() %s", keyValuePairs.string());
-
- if (param.getInt(key, device) == NO_ERROR) {
- mDevice = device;
- param.remove(key);
- }
-
- if (param.size()) {
- status = BAD_VALUE;
- }
- return status;
-}
-
-String8 AudioStreamOutGeneric::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- String8 value;
- String8 key = String8(AudioParameter::keyRouting);
-
- if (param.get(key, value) == NO_ERROR) {
- param.addInt(key, (int)mDevice);
- }
-
- LOGV("getParameters() %s", param.toString().string());
- return param.toString();
-}
-
-status_t AudioStreamOutGeneric::getRenderPosition(uint32_t *dspFrames)
-{
- return INVALID_OPERATION;
-}
-
-// ----------------------------------------------------------------------------
-
-// record functions
-status_t AudioStreamInGeneric::set(
- AudioHardwareGeneric *hw,
- int fd,
- uint32_t devices,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate,
- AudioSystem::audio_in_acoustics acoustics)
-{
- if (pFormat == 0 || pChannels == 0 || pRate == 0) return BAD_VALUE;
- LOGV("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, *pFormat, *pChannels, *pRate);
- // check values
- if ((*pFormat != format()) ||
- (*pChannels != channels()) ||
- (*pRate != sampleRate())) {
- LOGE("Error opening input channel");
- *pFormat = format();
- *pChannels = channels();
- *pRate = sampleRate();
- return BAD_VALUE;
- }
-
- mAudioHardware = hw;
- mFd = fd;
- mDevice = devices;
- return NO_ERROR;
-}
-
-AudioStreamInGeneric::~AudioStreamInGeneric()
-{
-}
-
-ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes)
-{
- AutoMutex lock(mLock);
- if (mFd < 0) {
- LOGE("Attempt to read from unopened device");
- return NO_INIT;
- }
- return ::read(mFd, buffer, bytes);
-}
-
-status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioStreamInGeneric::dump\n");
- result.append(buffer);
- snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tformat: %d\n", format());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice);
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware);
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioStreamInGeneric::setParameters(const String8& keyValuePairs)
-{
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 key = String8(AudioParameter::keyRouting);
- status_t status = NO_ERROR;
- int device;
- LOGV("setParameters() %s", keyValuePairs.string());
-
- if (param.getInt(key, device) == NO_ERROR) {
- mDevice = device;
- param.remove(key);
- }
-
- if (param.size()) {
- status = BAD_VALUE;
- }
- return status;
-}
-
-String8 AudioStreamInGeneric::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- String8 value;
- String8 key = String8(AudioParameter::keyRouting);
-
- if (param.get(key, value) == NO_ERROR) {
- param.addInt(key, (int)mDevice);
- }
-
- LOGV("getParameters() %s", param.toString().string());
- return param.toString();
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/audioflinger/AudioHardwareGeneric.h b/services/audioflinger/AudioHardwareGeneric.h
deleted file mode 100644
index aa4e78d..0000000
--- a/services/audioflinger/AudioHardwareGeneric.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
-**
-** Copyright 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_AUDIO_HARDWARE_GENERIC_H
-#define ANDROID_AUDIO_HARDWARE_GENERIC_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-
-#include <hardware_legacy/AudioHardwareBase.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class AudioHardwareGeneric;
-
-class AudioStreamOutGeneric : public AudioStreamOut {
-public:
- AudioStreamOutGeneric() : mAudioHardware(0), mFd(-1) {}
- virtual ~AudioStreamOutGeneric();
-
- virtual status_t set(
- AudioHardwareGeneric *hw,
- int mFd,
- uint32_t devices,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate);
-
- virtual uint32_t sampleRate() const { return 44100; }
- virtual size_t bufferSize() const { return 4096; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual uint32_t latency() const { return 20; }
- virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
- virtual ssize_t write(const void* buffer, size_t bytes);
- virtual status_t standby();
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual status_t getRenderPosition(uint32_t *dspFrames);
-
-private:
- AudioHardwareGeneric *mAudioHardware;
- Mutex mLock;
- int mFd;
- uint32_t mDevice;
-};
-
-class AudioStreamInGeneric : public AudioStreamIn {
-public:
- AudioStreamInGeneric() : mAudioHardware(0), mFd(-1) {}
- virtual ~AudioStreamInGeneric();
-
- virtual status_t set(
- AudioHardwareGeneric *hw,
- int mFd,
- uint32_t devices,
- int *pFormat,
- uint32_t *pChannels,
- uint32_t *pRate,
- AudioSystem::audio_in_acoustics acoustics);
-
- virtual uint32_t sampleRate() const { return 8000; }
- virtual size_t bufferSize() const { return 320; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual status_t setGain(float gain) { return INVALID_OPERATION; }
- virtual ssize_t read(void* buffer, ssize_t bytes);
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t standby() { return NO_ERROR; }
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
- virtual unsigned int getInputFramesLost() const { return 0; }
-
-private:
- AudioHardwareGeneric *mAudioHardware;
- Mutex mLock;
- int mFd;
- uint32_t mDevice;
-};
-
-
-class AudioHardwareGeneric : public AudioHardwareBase
-{
-public:
- AudioHardwareGeneric();
- virtual ~AudioHardwareGeneric();
- virtual status_t initCheck();
- virtual status_t setVoiceVolume(float volume);
- virtual status_t setMasterVolume(float volume);
-
- // mic mute
- virtual status_t setMicMute(bool state);
- virtual status_t getMicMute(bool* state);
-
- // create I/O streams
- virtual AudioStreamOut* openOutputStream(
- uint32_t devices,
- int *format=0,
- uint32_t *channels=0,
- uint32_t *sampleRate=0,
- status_t *status=0);
- virtual void closeOutputStream(AudioStreamOut* out);
-
- 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);
-
- void closeOutputStream(AudioStreamOutGeneric* out);
- void closeInputStream(AudioStreamInGeneric* in);
-protected:
- virtual status_t dump(int fd, const Vector<String16>& args);
-
-private:
- status_t dumpInternals(int fd, const Vector<String16>& args);
-
- Mutex mLock;
- AudioStreamOutGeneric *mOutput;
- AudioStreamInGeneric *mInput;
- int mFd;
- bool mMicMute;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_AUDIO_HARDWARE_GENERIC_H
diff --git a/services/audioflinger/AudioHardwareInterface.cpp b/services/audioflinger/AudioHardwareInterface.cpp
deleted file mode 100644
index f58e4c0..0000000
--- a/services/audioflinger/AudioHardwareInterface.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
-**
-** Copyright 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 <cutils/properties.h>
-#include <string.h>
-#include <unistd.h>
-//#define LOG_NDEBUG 0
-
-#define LOG_TAG "AudioHardwareInterface"
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include "AudioHardwareStub.h"
-#include "AudioHardwareGeneric.h"
-#ifdef WITH_A2DP
-#include "A2dpAudioInterface.h"
-#endif
-
-#ifdef ENABLE_AUDIO_DUMP
-#include "AudioDumpInterface.h"
-#endif
-
-
-// change to 1 to log routing calls
-#define LOG_ROUTING_CALLS 1
-
-namespace android {
-
-#if LOG_ROUTING_CALLS
-static const char* routingModeStrings[] =
-{
- "OUT OF RANGE",
- "INVALID",
- "CURRENT",
- "NORMAL",
- "RINGTONE",
- "IN_CALL",
- "IN_COMMUNICATION"
-};
-
-static const char* routeNone = "NONE";
-
-static const char* displayMode(int mode)
-{
- if ((mode < AudioSystem::MODE_INVALID) || (mode >= AudioSystem::NUM_MODES))
- return routingModeStrings[0];
- return routingModeStrings[mode+3];
-}
-#endif
-
-// ----------------------------------------------------------------------------
-
-AudioHardwareInterface* AudioHardwareInterface::create()
-{
- /*
- * FIXME: This code needs to instantiate the correct audio device
- * interface. For now - we use compile-time switches.
- */
- AudioHardwareInterface* hw = 0;
- char value[PROPERTY_VALUE_MAX];
-
-#ifdef GENERIC_AUDIO
- hw = new AudioHardwareGeneric();
-#else
- // if running in emulation - use the emulator driver
- if (property_get("ro.kernel.qemu", value, 0)) {
- LOGD("Running in emulation - using generic audio driver");
- hw = new AudioHardwareGeneric();
- }
- else {
- LOGV("Creating Vendor Specific AudioHardware");
- hw = createAudioHardware();
- }
-#endif
- if (hw->initCheck() != NO_ERROR) {
- LOGW("Using stubbed audio hardware. No sound will be produced.");
- delete hw;
- hw = new AudioHardwareStub();
- }
-
-#ifdef WITH_A2DP
- hw = new A2dpAudioInterface(hw);
-#endif
-
-#ifdef ENABLE_AUDIO_DUMP
- // This code adds a record of buffers in a file to write calls made by AudioFlinger.
- // It replaces the current AudioHardwareInterface object by an intermediate one which
- // will record buffers in a file (after sending them to hardware) for testing purpose.
- // This feature is enabled by defining symbol ENABLE_AUDIO_DUMP.
- // The output file is set with setParameters("test_cmd_file_name=<name>"). Pause are not recorded in the file.
- LOGV("opening PCM dump interface");
- hw = new AudioDumpInterface(hw); // replace interface
-#endif
- return hw;
-}
-
-AudioStreamOut::~AudioStreamOut()
-{
-}
-
-AudioStreamIn::~AudioStreamIn() {}
-
-AudioHardwareBase::AudioHardwareBase()
-{
- mMode = 0;
-}
-
-status_t AudioHardwareBase::setMode(int mode)
-{
-#if LOG_ROUTING_CALLS
- LOGD("setMode(%s)", displayMode(mode));
-#endif
- if ((mode < 0) || (mode >= AudioSystem::NUM_MODES))
- return BAD_VALUE;
- if (mMode == mode)
- return ALREADY_EXISTS;
- mMode = mode;
- return NO_ERROR;
-}
-
-// default implementation
-status_t AudioHardwareBase::setParameters(const String8& keyValuePairs)
-{
- return NO_ERROR;
-}
-
-// default implementation
-String8 AudioHardwareBase::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-// default implementation
-size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
-{
- if (sampleRate != 8000) {
- LOGW("getInputBufferSize bad sampling rate: %d", sampleRate);
- return 0;
- }
- if (format != AudioSystem::PCM_16_BIT) {
- LOGW("getInputBufferSize bad format: %d", format);
- return 0;
- }
- if (channelCount != 1) {
- LOGW("getInputBufferSize bad channel count: %d", channelCount);
- return 0;
- }
-
- return 320;
-}
-
-status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n");
- result.append(buffer);
- snprintf(buffer, SIZE, "\tmMode: %d\n", mMode);
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- dump(fd, args); // Dump the state of the concrete child.
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/audioflinger/AudioHardwareStub.cpp b/services/audioflinger/AudioHardwareStub.cpp
deleted file mode 100644
index d481150..0000000
--- a/services/audioflinger/AudioHardwareStub.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/* //device/servers/AudioFlinger/AudioHardwareStub.cpp
-**
-** Copyright 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 <stdint.h>
-#include <sys/types.h>
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <utils/String8.h>
-
-#include "AudioHardwareStub.h"
-#include <media/AudioRecord.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-AudioHardwareStub::AudioHardwareStub() : mMicMute(false)
-{
-}
-
-AudioHardwareStub::~AudioHardwareStub()
-{
-}
-
-status_t AudioHardwareStub::initCheck()
-{
- return NO_ERROR;
-}
-
-AudioStreamOut* AudioHardwareStub::openOutputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
-{
- AudioStreamOutStub* out = new AudioStreamOutStub();
- status_t lStatus = out->set(format, channels, sampleRate);
- if (status) {
- *status = lStatus;
- }
- if (lStatus == NO_ERROR)
- return out;
- delete out;
- return 0;
-}
-
-void AudioHardwareStub::closeOutputStream(AudioStreamOut* out)
-{
- delete out;
-}
-
-AudioStreamIn* AudioHardwareStub::openInputStream(
- uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate,
- status_t *status, AudioSystem::audio_in_acoustics acoustics)
-{
- // check for valid input source
- if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
- return 0;
- }
-
- AudioStreamInStub* in = new AudioStreamInStub();
- status_t lStatus = in->set(format, channels, sampleRate, acoustics);
- if (status) {
- *status = lStatus;
- }
- if (lStatus == NO_ERROR)
- return in;
- delete in;
- return 0;
-}
-
-void AudioHardwareStub::closeInputStream(AudioStreamIn* in)
-{
- delete in;
-}
-
-status_t AudioHardwareStub::setVoiceVolume(float volume)
-{
- return NO_ERROR;
-}
-
-status_t AudioHardwareStub::setMasterVolume(float volume)
-{
- return NO_ERROR;
-}
-
-status_t AudioHardwareStub::dumpInternals(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- result.append("AudioHardwareStub::dumpInternals\n");
- snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false");
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args)
-{
- dumpInternals(fd, args);
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t AudioStreamOutStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate)
-{
- if (pFormat) *pFormat = format();
- if (pChannels) *pChannels = channels();
- if (pRate) *pRate = sampleRate();
-
- return NO_ERROR;
-}
-
-ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes)
-{
- // fake timing for audio output
- usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate());
- return bytes;
-}
-
-status_t AudioStreamOutStub::standby()
-{
- return NO_ERROR;
-}
-
-status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n");
- snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
- snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
- snprintf(buffer, SIZE, "\tformat: %d\n", format());
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-String8 AudioStreamOutStub::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-status_t AudioStreamOutStub::getRenderPosition(uint32_t *dspFrames)
-{
- return INVALID_OPERATION;
-}
-
-// ----------------------------------------------------------------------------
-
-status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate,
- AudioSystem::audio_in_acoustics acoustics)
-{
- return NO_ERROR;
-}
-
-ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes)
-{
- // fake timing for audio input
- usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate());
- memset(buffer, 0, bytes);
- return bytes;
-}
-
-status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, SIZE, "AudioStreamInStub::dump\n");
- result.append(buffer);
- snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
- result.append(buffer);
- snprintf(buffer, SIZE, "\tformat: %d\n", format());
- result.append(buffer);
- ::write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-String8 AudioStreamInStub::getParameters(const String8& keys)
-{
- AudioParameter param = AudioParameter(keys);
- return param.toString();
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/audioflinger/AudioHardwareStub.h b/services/audioflinger/AudioHardwareStub.h
deleted file mode 100644
index 06a29de..0000000
--- a/services/audioflinger/AudioHardwareStub.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* //device/servers/AudioFlinger/AudioHardwareStub.h
-**
-** Copyright 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_AUDIO_HARDWARE_STUB_H
-#define ANDROID_AUDIO_HARDWARE_STUB_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <hardware_legacy/AudioHardwareBase.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class AudioStreamOutStub : public AudioStreamOut {
-public:
- virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate);
- virtual uint32_t sampleRate() const { return 44100; }
- virtual size_t bufferSize() const { return 4096; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual uint32_t latency() const { return 0; }
- virtual status_t setVolume(float left, float right) { return NO_ERROR; }
- virtual ssize_t write(const void* buffer, size_t bytes);
- virtual status_t standby();
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;}
- virtual String8 getParameters(const String8& keys);
- virtual status_t getRenderPosition(uint32_t *dspFrames);
-};
-
-class AudioStreamInStub : public AudioStreamIn {
-public:
- virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics);
- virtual uint32_t sampleRate() const { return 8000; }
- virtual size_t bufferSize() const { return 320; }
- virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
- virtual int format() const { return AudioSystem::PCM_16_BIT; }
- virtual status_t setGain(float gain) { return NO_ERROR; }
- virtual ssize_t read(void* buffer, ssize_t bytes);
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t standby() { return NO_ERROR; }
- virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;}
- virtual String8 getParameters(const String8& keys);
- virtual unsigned int getInputFramesLost() const { return 0; }
-};
-
-class AudioHardwareStub : public AudioHardwareBase
-{
-public:
- AudioHardwareStub();
- virtual ~AudioHardwareStub();
- virtual status_t initCheck();
- virtual status_t setVoiceVolume(float volume);
- virtual status_t setMasterVolume(float volume);
-
- // mic mute
- virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; }
- virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; }
-
- // create I/O streams
- virtual AudioStreamOut* openOutputStream(
- uint32_t devices,
- int *format=0,
- uint32_t *channels=0,
- uint32_t *sampleRate=0,
- status_t *status=0);
- virtual void closeOutputStream(AudioStreamOut* out);
-
- 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);
-
-protected:
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- bool mMicMute;
-private:
- status_t dumpInternals(int fd, const Vector<String16>& args);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_AUDIO_HARDWARE_STUB_H
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
deleted file mode 100644
index f5e7343..0000000
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ /dev/null
@@ -1,2286 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "AudioPolicyManagerBase"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-#include <hardware_legacy/AudioPolicyManagerBase.h>
-#include <media/mediarecorder.h>
-#include <math.h>
-
-namespace android {
-
-
-// ----------------------------------------------------------------------------
-// AudioPolicyInterface implementation
-// ----------------------------------------------------------------------------
-
-
-status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
- const char *device_address)
-{
-
- LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);
-
- // connect/disconnect only 1 device at a time
- if (AudioSystem::popCount(device) != 1) return BAD_VALUE;
-
- if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
- LOGE("setDeviceConnectionState() invalid address: %s", device_address);
- return BAD_VALUE;
- }
-
- // handle output devices
- if (AudioSystem::isOutputDevice(device)) {
-
-#ifndef WITH_A2DP
- if (AudioSystem::isA2dpDevice(device)) {
- LOGE("setDeviceConnectionState() invalid device: %x", device);
- return BAD_VALUE;
- }
-#endif
-
- switch (state)
- {
- // handle output device connection
- case AudioSystem::DEVICE_STATE_AVAILABLE:
- if (mAvailableOutputDevices & device) {
- LOGW("setDeviceConnectionState() device already connected: %x", device);
- return INVALID_OPERATION;
- }
- LOGV("setDeviceConnectionState() connecting device %x", device);
-
- // register new device as available
- mAvailableOutputDevices |= device;
-
-#ifdef WITH_A2DP
- // handle A2DP device connection
- if (AudioSystem::isA2dpDevice(device)) {
- status_t status = handleA2dpConnection(device, device_address);
- if (status != NO_ERROR) {
- mAvailableOutputDevices &= ~device;
- return status;
- }
- } else
-#endif
- {
- if (AudioSystem::isBluetoothScoDevice(device)) {
- LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address);
- // keep track of SCO device address
- mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
- }
- }
- break;
- // handle output device disconnection
- case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
- if (!(mAvailableOutputDevices & device)) {
- LOGW("setDeviceConnectionState() device not connected: %x", device);
- return INVALID_OPERATION;
- }
-
-
- LOGV("setDeviceConnectionState() disconnecting device %x", device);
- // remove device from available output devices
- mAvailableOutputDevices &= ~device;
-
-#ifdef WITH_A2DP
- // handle A2DP device disconnection
- if (AudioSystem::isA2dpDevice(device)) {
- status_t status = handleA2dpDisconnection(device, device_address);
- if (status != NO_ERROR) {
- mAvailableOutputDevices |= device;
- return status;
- }
- } else
-#endif
- {
- if (AudioSystem::isBluetoothScoDevice(device)) {
- mScoDeviceAddress = "";
- }
- }
- } break;
-
- default:
- LOGE("setDeviceConnectionState() invalid state: %x", state);
- return BAD_VALUE;
- }
-
- // request routing change if necessary
- uint32_t newDevice = getNewDevice(mHardwareOutput, false);
-#ifdef WITH_A2DP
- checkA2dpSuspend();
- checkOutputForAllStrategies();
- // A2DP outputs must be closed after checkOutputForAllStrategies() is executed
- if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
- closeA2dpOutputs();
- }
-#endif
- updateDeviceForStrategy();
- setOutputDevice(mHardwareOutput, newDevice);
-
- if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
- device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
- } else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO ||
- device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
- device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
- device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- } else {
- return NO_ERROR;
- }
- }
- // handle input devices
- if (AudioSystem::isInputDevice(device)) {
-
- switch (state)
- {
- // handle input device connection
- case AudioSystem::DEVICE_STATE_AVAILABLE: {
- if (mAvailableInputDevices & device) {
- LOGW("setDeviceConnectionState() device already connected: %d", device);
- return INVALID_OPERATION;
- }
- mAvailableInputDevices |= device;
- }
- break;
-
- // handle input device disconnection
- case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
- if (!(mAvailableInputDevices & device)) {
- LOGW("setDeviceConnectionState() device not connected: %d", device);
- return INVALID_OPERATION;
- }
- mAvailableInputDevices &= ~device;
- } break;
-
- default:
- LOGE("setDeviceConnectionState() invalid state: %x", state);
- return BAD_VALUE;
- }
-
- audio_io_handle_t activeInput = getActiveInput();
- if (activeInput != 0) {
- AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
- uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
- if (newDevice != inputDesc->mDevice) {
- LOGV("setDeviceConnectionState() 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());
- }
- }
-
- return NO_ERROR;
- }
-
- LOGW("setDeviceConnectionState() invalid device: %x", device);
- return BAD_VALUE;
-}
-
-AudioSystem::device_connection_state AudioPolicyManagerBase::getDeviceConnectionState(AudioSystem::audio_devices device,
- const char *device_address)
-{
- AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE;
- String8 address = String8(device_address);
- if (AudioSystem::isOutputDevice(device)) {
- if (device & mAvailableOutputDevices) {
-#ifdef WITH_A2DP
- if (AudioSystem::isA2dpDevice(device) &&
- address != "" && mA2dpDeviceAddress != address) {
- return state;
- }
-#endif
- if (AudioSystem::isBluetoothScoDevice(device) &&
- address != "" && mScoDeviceAddress != address) {
- return state;
- }
- state = AudioSystem::DEVICE_STATE_AVAILABLE;
- }
- } else if (AudioSystem::isInputDevice(device)) {
- if (device & mAvailableInputDevices) {
- state = AudioSystem::DEVICE_STATE_AVAILABLE;
- }
- }
-
- return state;
-}
-
-void AudioPolicyManagerBase::setPhoneState(int state)
-{
- LOGV("setPhoneState() state %d", state);
- uint32_t newDevice = 0;
- if (state < 0 || state >= AudioSystem::NUM_MODES) {
- LOGW("setPhoneState() invalid state %d", state);
- return;
- }
-
- if (state == mPhoneState ) {
- LOGW("setPhoneState() setting same state %d", state);
- return;
- }
-
- // if leaving call state, handle special case of active streams
- // pertaining to sonification strategy see handleIncallSonification()
- if (isInCall()) {
- LOGV("setPhoneState() in call state management: new state is %d", state);
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- handleIncallSonification(stream, false, true);
- }
- }
-
- // store previous phone state for management of sonification strategy below
- int oldState = mPhoneState;
- mPhoneState = state;
- bool force = false;
-
- // are we entering or starting a call
- if (!isStateInCall(oldState) && isStateInCall(state)) {
- LOGV(" Entering call in setPhoneState()");
- // force routing command to audio hardware when starting a call
- // even if no device change is needed
- force = true;
- } else if (isStateInCall(oldState) && !isStateInCall(state)) {
- LOGV(" Exiting call in setPhoneState()");
- // force routing command to audio hardware when exiting a call
- // even if no device change is needed
- force = true;
- } else if (isStateInCall(state) && (state != oldState)) {
- LOGV(" Switching between telephony and VoIP in setPhoneState()");
- // force routing command to audio hardware when switching between telephony and VoIP
- // even if no device change is needed
- force = true;
- }
-
- // check for device and output changes triggered by new phone state
- newDevice = getNewDevice(mHardwareOutput, false);
-#ifdef WITH_A2DP
- checkA2dpSuspend();
- checkOutputForAllStrategies();
-#endif
- updateDeviceForStrategy();
-
- AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
-
- // force routing command to audio hardware when ending call
- // even if no device change is needed
- if (isStateInCall(oldState) && newDevice == 0) {
- newDevice = hwOutputDesc->device();
- }
-
- // when changing from ring tone to in call mode, mute the ringing tone
- // immediately and delay the route change to avoid sending the ring tone
- // tail into the earpiece or headset.
- int delayMs = 0;
- if (isStateInCall(state) && oldState == AudioSystem::MODE_RINGTONE) {
- // delay the device change command by twice the output latency to have some margin
- // and be sure that audio buffers not yet affected by the mute are out when
- // we actually apply the route change
- delayMs = hwOutputDesc->mLatency*2;
- setStreamMute(AudioSystem::RING, true, mHardwareOutput);
- }
-
- // change routing is necessary
- setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
-
- // if entering in call state, handle special case of active streams
- // pertaining to sonification strategy see handleIncallSonification()
- if (isStateInCall(state)) {
- LOGV("setPhoneState() in call state management: new state is %d", state);
- // unmute the ringing tone after a sufficient delay if it was muted before
- // setting output device above
- if (oldState == AudioSystem::MODE_RINGTONE) {
- setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
- }
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- handleIncallSonification(stream, true, true);
- }
- }
-
- // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
- if (state == AudioSystem::MODE_RINGTONE &&
- isStreamActive(AudioSystem::MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) {
- mLimitRingtoneVolume = true;
- } else {
- mLimitRingtoneVolume = false;
- }
-}
-
-void AudioPolicyManagerBase::setRingerMode(uint32_t mode, uint32_t mask)
-{
- LOGV("setRingerMode() mode %x, mask %x", mode, mask);
-
- mRingerMode = mode;
-}
-
-void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
-{
- LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
-
- bool forceVolumeReeval = false;
- switch(usage) {
- case AudioSystem::FOR_COMMUNICATION:
- if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO &&
- config != AudioSystem::FORCE_NONE) {
- LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
- return;
- }
- forceVolumeReeval = true;
- mForceUse[usage] = config;
- break;
- case AudioSystem::FOR_MEDIA:
- if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP &&
- config != AudioSystem::FORCE_WIRED_ACCESSORY &&
- config != AudioSystem::FORCE_ANALOG_DOCK &&
- config != AudioSystem::FORCE_DIGITAL_DOCK && config != AudioSystem::FORCE_NONE) {
- LOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
- return;
- }
- mForceUse[usage] = config;
- break;
- case AudioSystem::FOR_RECORD:
- if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY &&
- config != AudioSystem::FORCE_NONE) {
- LOGW("setForceUse() invalid config %d for FOR_RECORD", config);
- return;
- }
- mForceUse[usage] = config;
- break;
- case AudioSystem::FOR_DOCK:
- if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
- config != AudioSystem::FORCE_BT_DESK_DOCK &&
- config != AudioSystem::FORCE_WIRED_ACCESSORY &&
- config != AudioSystem::FORCE_ANALOG_DOCK &&
- config != AudioSystem::FORCE_DIGITAL_DOCK) {
- LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
- }
- forceVolumeReeval = true;
- mForceUse[usage] = config;
- break;
- default:
- LOGW("setForceUse() invalid usage %d", usage);
- break;
- }
-
- // check for device and output changes triggered by new phone state
- uint32_t newDevice = getNewDevice(mHardwareOutput, false);
-#ifdef WITH_A2DP
- checkA2dpSuspend();
- checkOutputForAllStrategies();
-#endif
- updateDeviceForStrategy();
- setOutputDevice(mHardwareOutput, newDevice);
- if (forceVolumeReeval) {
- applyStreamVolumes(mHardwareOutput, newDevice, 0, true);
- }
-
- 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)
-{
- return mForceUse[usage];
-}
-
-void AudioPolicyManagerBase::setSystemProperty(const char* property, const char* value)
-{
- LOGV("setSystemProperty() property %s, value %s", property, value);
- if (strcmp(property, "ro.camera.sound.forced") == 0) {
- if (atoi(value)) {
- LOGV("ENFORCED_AUDIBLE cannot be muted");
- mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;
- } else {
- LOGV("ENFORCED_AUDIBLE can be muted");
- mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true;
- }
- }
-}
-
-audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream,
- uint32_t samplingRate,
- uint32_t format,
- uint32_t channels,
- AudioSystem::output_flags flags)
-{
- audio_io_handle_t output = 0;
- uint32_t latency = 0;
- routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
- uint32_t device = getDeviceForStrategy(strategy);
- LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags);
-
-#ifdef AUDIO_POLICY_TEST
- if (mCurOutput != 0) {
- LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d",
- mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
-
- if (mTestOutputs[mCurOutput] == 0) {
- LOGV("getOutput() opening test output");
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = mTestDevice;
- outputDesc->mSamplingRate = mTestSamplingRate;
- outputDesc->mFormat = mTestFormat;
- outputDesc->mChannels = mTestChannels;
- outputDesc->mLatency = mTestLatencyMs;
- outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
- outputDesc->mRefCount[stream] = 0;
- mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
- if (mTestOutputs[mCurOutput]) {
- AudioParameter outputCmd = AudioParameter();
- outputCmd.addInt(String8("set_id"),mCurOutput);
- mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString());
- addOutput(mTestOutputs[mCurOutput], outputDesc);
- }
- }
- return mTestOutputs[mCurOutput];
- }
-#endif //AUDIO_POLICY_TEST
-
- // open a direct output if required by specified parameters
- if (needsDirectOuput(stream, samplingRate, format, channels, flags, device)) {
-
- LOGV("getOutput() opening direct output device %x", device);
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = device;
- outputDesc->mSamplingRate = samplingRate;
- outputDesc->mFormat = format;
- outputDesc->mChannels = channels;
- outputDesc->mLatency = 0;
- outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT);
- outputDesc->mRefCount[stream] = 0;
- outputDesc->mStopTime[stream] = 0;
- output = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
-
- // only accept an output with the requeted parameters
- if (output == 0 ||
- (samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) ||
- (format != 0 && format != outputDesc->mFormat) ||
- (channels != 0 && channels != outputDesc->mChannels)) {
- LOGV("getOutput() failed opening direct output: samplingRate %d, format %d, channels %d",
- samplingRate, format, channels);
- if (output != 0) {
- mpClientInterface->closeOutput(output);
- }
- delete outputDesc;
- return 0;
- }
- addOutput(output, outputDesc);
- return output;
- }
-
- if (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO &&
- channels != AudioSystem::CHANNEL_OUT_STEREO) {
- return 0;
- }
- // open a non direct output
-
- // get which output is suitable for the specified stream. The actual routing change will happen
- // when startOutput() will be called
- uint32_t a2dpDevice = device & AudioSystem::DEVICE_OUT_ALL_A2DP;
- if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) {
-#ifdef WITH_A2DP
- if (a2dpUsedForSonification() && a2dpDevice != 0) {
- // if playing on 2 devices among which one is A2DP, use duplicated output
- LOGV("getOutput() using duplicated output");
- LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device in multiple %x selected but A2DP output not opened", device);
- output = mDuplicatedOutput;
- } else
-#endif
- {
- // if playing on 2 devices among which none is A2DP, use hardware output
- output = mHardwareOutput;
- }
- LOGV("getOutput() using output %d for 2 devices %x", output, device);
- } else {
-#ifdef WITH_A2DP
- if (a2dpDevice != 0) {
- // if playing on A2DP device, use a2dp output
- LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device %x selected but A2DP output not opened", device);
- output = mA2dpOutput;
- } else
-#endif
- {
- // if playing on not A2DP device, use hardware output
- output = mHardwareOutput;
- }
- }
-
-
- LOGW_IF((output ==0), "getOutput() could not find output for stream %d, samplingRate %d, format %d, channels %x, flags %x",
- stream, samplingRate, format, channels, flags);
-
- return output;
-}
-
-status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
- int session)
-{
- 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);
- return BAD_VALUE;
- }
-
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
- routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
-
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
- setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
- }
-#endif
-
- // incremenent usage count for this stream on the requested output:
- // NOTE that the usage count is the same for duplicated output and hardware output which is
- // necassary for a correct control of hardware output routing by startOutput() and stopOutput()
- outputDesc->changeRefCount(stream, 1);
-
- setOutputDevice(output, getNewDevice(output));
-
- // handle special case for sonification while in call
- if (isInCall()) {
- handleIncallSonification(stream, true, false);
- }
-
- // apply volume rules for current stream and device if necessary
- checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device());
-
- return NO_ERROR;
-}
-
-status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
- int session)
-{
- 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);
- return BAD_VALUE;
- }
-
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
- routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
-
- // handle special case for sonification while in call
- if (isInCall()) {
- handleIncallSonification(stream, false, false);
- }
-
- if (outputDesc->mRefCount[stream] > 0) {
- // decrement usage count of this stream on the output
- outputDesc->changeRefCount(stream, -1);
- // store time at which the stream was stopped - see isStreamActive()
- outputDesc->mStopTime[stream] = systemTime();
-
- setOutputDevice(output, getNewDevice(output), false, outputDesc->mLatency*2);
-
-#ifdef WITH_A2DP
- if (mA2dpOutput != 0 && !a2dpUsedForSonification() &&
- strategy == STRATEGY_SONIFICATION) {
- setStrategyMute(STRATEGY_MEDIA,
- false,
- mA2dpOutput,
- mOutputs.valueFor(mHardwareOutput)->mLatency*2);
- }
-#endif
- if (output != mHardwareOutput) {
- setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true);
- }
- return NO_ERROR;
- } else {
- LOGW("stopOutput() refcount is already 0 for output %d", output);
- return INVALID_OPERATION;
- }
-}
-
-void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output)
-{
- LOGV("releaseOutput() %d", output);
- ssize_t index = mOutputs.indexOfKey(output);
- if (index < 0) {
- LOGW("releaseOutput() releasing unknown output %d", output);
- return;
- }
-
-#ifdef AUDIO_POLICY_TEST
- int testIndex = testOutputIndex(output);
- if (testIndex != 0) {
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
- if (outputDesc->refCount() == 0) {
- mpClientInterface->closeOutput(output);
- delete mOutputs.valueAt(index);
- mOutputs.removeItem(output);
- mTestOutputs[testIndex] = 0;
- }
- return;
- }
-#endif //AUDIO_POLICY_TEST
-
- if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) {
- mpClientInterface->closeOutput(output);
- delete mOutputs.valueAt(index);
- mOutputs.removeItem(output);
- }
-}
-
-audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource,
- uint32_t samplingRate,
- uint32_t format,
- uint32_t channels,
- AudioSystem::audio_in_acoustics acoustics)
-{
- audio_io_handle_t input = 0;
- uint32_t device = getDeviceForInputSource(inputSource);
-
- LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics);
-
- if (device == 0) {
- return 0;
- }
-
- // adapt channel selection to input source
- switch(inputSource) {
- case AUDIO_SOURCE_VOICE_UPLINK:
- channels = AudioSystem::CHANNEL_IN_VOICE_UPLINK;
- break;
- case AUDIO_SOURCE_VOICE_DOWNLINK:
- channels = AudioSystem::CHANNEL_IN_VOICE_DNLINK;
- break;
- case AUDIO_SOURCE_VOICE_CALL:
- channels = (AudioSystem::CHANNEL_IN_VOICE_UPLINK | AudioSystem::CHANNEL_IN_VOICE_DNLINK);
- break;
- default:
- break;
- }
-
- AudioInputDescriptor *inputDesc = new AudioInputDescriptor();
-
- inputDesc->mInputSource = inputSource;
- inputDesc->mDevice = device;
- inputDesc->mSamplingRate = samplingRate;
- inputDesc->mFormat = format;
- inputDesc->mChannels = channels;
- inputDesc->mAcoustics = acoustics;
- inputDesc->mRefCount = 0;
- input = mpClientInterface->openInput(&inputDesc->mDevice,
- &inputDesc->mSamplingRate,
- &inputDesc->mFormat,
- &inputDesc->mChannels,
- inputDesc->mAcoustics);
-
- // only accept input with the exact requested set of parameters
- if (input == 0 ||
- (samplingRate != inputDesc->mSamplingRate) ||
- (format != inputDesc->mFormat) ||
- (channels != inputDesc->mChannels)) {
- LOGV("getInput() failed opening input: samplingRate %d, format %d, channels %d",
- samplingRate, format, channels);
- if (input != 0) {
- mpClientInterface->closeInput(input);
- }
- delete inputDesc;
- return 0;
- }
- mInputs.add(input, inputDesc);
- return input;
-}
-
-status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input)
-{
- LOGV("startInput() input %d", input);
- ssize_t index = mInputs.indexOfKey(input);
- if (index < 0) {
- LOGW("startInput() unknow input %d", input);
- return BAD_VALUE;
- }
- AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
-
-#ifdef AUDIO_POLICY_TEST
- if (mTestInput == 0)
-#endif //AUDIO_POLICY_TEST
- {
- // refuse 2 active AudioRecord clients at the same time
- if (getActiveInput() != 0) {
- LOGW("startInput() input %d failed: other input already started", input);
- return INVALID_OPERATION;
- }
- }
-
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
-
- param.addInt(String8(AudioParameter::keyInputSource), (int)inputDesc->mInputSource);
- LOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource);
-
- mpClientInterface->setParameters(input, param.toString());
-
- inputDesc->mRefCount = 1;
- return NO_ERROR;
-}
-
-status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input)
-{
- LOGV("stopInput() input %d", input);
- ssize_t index = mInputs.indexOfKey(input);
- if (index < 0) {
- LOGW("stopInput() unknow input %d", input);
- return BAD_VALUE;
- }
- AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
-
- if (inputDesc->mRefCount == 0) {
- LOGW("stopInput() input %d already stopped", input);
- return INVALID_OPERATION;
- } else {
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), 0);
- mpClientInterface->setParameters(input, param.toString());
- inputDesc->mRefCount = 0;
- return NO_ERROR;
- }
-}
-
-void AudioPolicyManagerBase::releaseInput(audio_io_handle_t input)
-{
- LOGV("releaseInput() %d", input);
- ssize_t index = mInputs.indexOfKey(input);
- if (index < 0) {
- LOGW("releaseInput() releasing unknown input %d", input);
- return;
- }
- mpClientInterface->closeInput(input);
- delete mInputs.valueAt(index);
- mInputs.removeItem(input);
- LOGV("releaseInput() exit");
-}
-
-void AudioPolicyManagerBase::initStreamVolume(AudioSystem::stream_type stream,
- int indexMin,
- int indexMax)
-{
- LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
- if (indexMin < 0 || indexMin >= indexMax) {
- LOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax);
- return;
- }
- mStreams[stream].mIndexMin = indexMin;
- mStreams[stream].mIndexMax = indexMax;
-}
-
-status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
-{
-
- if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
- return BAD_VALUE;
- }
-
- // Force max volume if stream cannot be muted
- if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
-
- LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index);
- mStreams[stream].mIndexCur = index;
-
- // compute and apply stream volume on all outputs according to connected device
- status_t status = NO_ERROR;
- for (size_t i = 0; i < mOutputs.size(); i++) {
- status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), mOutputs.valueAt(i)->device());
- if (volStatus != NO_ERROR) {
- status = volStatus;
- }
- }
- return status;
-}
-
-status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
-{
- if (index == 0) {
- return BAD_VALUE;
- }
- LOGV("getStreamVolumeIndex() stream %d", stream);
- *index = mStreams[stream].mIndexCur;
- 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;
-}
-
-bool AudioPolicyManagerBase::isStreamActive(int stream, uint32_t inPastMs) const
-{
- nsecs_t sysTime = systemTime();
- for (size_t i = 0; i < mOutputs.size(); i++) {
- if (mOutputs.valueAt(i)->mRefCount[stream] != 0 ||
- ns2ms(sysTime - mOutputs.valueAt(i)->mStopTime[stream]) < inPastMs) {
- return true;
- }
- }
- return false;
-}
-
-
-status_t AudioPolicyManagerBase::dump(int fd)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
- result.append(buffer);
- snprintf(buffer, SIZE, " Hardware Output: %d\n", mHardwareOutput);
- result.append(buffer);
-#ifdef WITH_A2DP
- snprintf(buffer, SIZE, " A2DP Output: %d\n", mA2dpOutput);
- result.append(buffer);
- snprintf(buffer, SIZE, " Duplicated Output: %d\n", mDuplicatedOutput);
- result.append(buffer);
- snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string());
- result.append(buffer);
-#endif
- snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string());
- result.append(buffer);
- snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices);
- result.append(buffer);
- snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices);
- result.append(buffer);
- snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState);
- result.append(buffer);
- snprintf(buffer, SIZE, " Ringer mode: %d\n", mRingerMode);
- result.append(buffer);
- snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]);
- result.append(buffer);
- snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]);
- result.append(buffer);
- snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]);
- result.append(buffer);
- snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]);
- result.append(buffer);
- write(fd, result.string(), result.size());
-
- snprintf(buffer, SIZE, "\nOutputs dump:\n");
- write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < mOutputs.size(); i++) {
- snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i));
- write(fd, buffer, strlen(buffer));
- mOutputs.valueAt(i)->dump(fd);
- }
-
- snprintf(buffer, SIZE, "\nInputs dump:\n");
- write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < mInputs.size(); i++) {
- snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i));
- write(fd, buffer, strlen(buffer));
- mInputs.valueAt(i)->dump(fd);
- }
-
- snprintf(buffer, SIZE, "\nStreams dump:\n");
- write(fd, buffer, strlen(buffer));
- snprintf(buffer, SIZE, " Stream Index Min Index Max Index Cur Can be muted\n");
- write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
- snprintf(buffer, SIZE, " %02d", i);
- mStreams[i].dump(buffer + 3, SIZE);
- 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;
-}
-
-// ----------------------------------------------------------------------------
-// AudioPolicyManagerBase
-// ----------------------------------------------------------------------------
-
-AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
- :
-#ifdef AUDIO_POLICY_TEST
- Thread(false),
-#endif //AUDIO_POLICY_TEST
- mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0),
- mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
- mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
- mA2dpSuspended(false)
-{
- mpClientInterface = clientInterface;
-
- for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
- mForceUse[i] = AudioSystem::FORCE_NONE;
- }
-
- initializeVolumeCurves();
-
- // devices available by default are speaker, ear piece and microphone
- mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
- AudioSystem::DEVICE_OUT_SPEAKER;
- mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
-
-#ifdef WITH_A2DP
- mA2dpOutput = 0;
- mDuplicatedOutput = 0;
- mA2dpDeviceAddress = String8("");
-#endif
- mScoDeviceAddress = String8("");
-
- // open hardware output
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
- mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
-
- if (mHardwareOutput == 0) {
- LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
- outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
- } else {
- addOutput(mHardwareOutput, outputDesc);
- setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
- //TODO: configure audio effect output stage here
- }
-
- updateDeviceForStrategy();
-#ifdef AUDIO_POLICY_TEST
- if (mHardwareOutput != 0) {
- AudioParameter outputCmd = AudioParameter();
- outputCmd.addInt(String8("set_id"), 0);
- mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
-
- mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER;
- mTestSamplingRate = 44100;
- mTestFormat = AudioSystem::PCM_16_BIT;
- mTestChannels = AudioSystem::CHANNEL_OUT_STEREO;
- mTestLatencyMs = 0;
- mCurOutput = 0;
- mDirectOutput = false;
- for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
- mTestOutputs[i] = 0;
- }
-
- const size_t SIZE = 256;
- char buffer[SIZE];
- snprintf(buffer, SIZE, "AudioPolicyManagerTest");
- run(buffer, ANDROID_PRIORITY_AUDIO);
- }
-#endif //AUDIO_POLICY_TEST
-}
-
-AudioPolicyManagerBase::~AudioPolicyManagerBase()
-{
-#ifdef AUDIO_POLICY_TEST
- exit();
-#endif //AUDIO_POLICY_TEST
- for (size_t i = 0; i < mOutputs.size(); i++) {
- mpClientInterface->closeOutput(mOutputs.keyAt(i));
- delete mOutputs.valueAt(i);
- }
- mOutputs.clear();
- for (size_t i = 0; i < mInputs.size(); i++) {
- mpClientInterface->closeInput(mInputs.keyAt(i));
- delete mInputs.valueAt(i);
- }
- mInputs.clear();
-}
-
-status_t AudioPolicyManagerBase::initCheck()
-{
- return (mHardwareOutput == 0) ? NO_INIT : NO_ERROR;
-}
-
-#ifdef AUDIO_POLICY_TEST
-bool AudioPolicyManagerBase::threadLoop()
-{
- LOGV("entering threadLoop()");
- while (!exitPending())
- {
- String8 command;
- int valueInt;
- String8 value;
-
- Mutex::Autolock _l(mLock);
- mWaitWorkCV.waitRelative(mLock, milliseconds(50));
-
- command = mpClientInterface->getParameters(0, String8("test_cmd_policy"));
- AudioParameter param = AudioParameter(command);
-
- if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR &&
- valueInt != 0) {
- LOGV("Test command %s received", command.string());
- String8 target;
- if (param.get(String8("target"), target) != NO_ERROR) {
- target = "Manager";
- }
- if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_output"));
- mCurOutput = valueInt;
- }
- if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_direct"));
- if (value == "false") {
- mDirectOutput = false;
- } else if (value == "true") {
- mDirectOutput = true;
- }
- }
- if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_input"));
- mTestInput = valueInt;
- }
-
- if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_format"));
- int format = AudioSystem::INVALID_FORMAT;
- if (value == "PCM 16 bits") {
- format = AudioSystem::PCM_16_BIT;
- } else if (value == "PCM 8 bits") {
- format = AudioSystem::PCM_8_BIT;
- } else if (value == "Compressed MP3") {
- format = AudioSystem::MP3;
- }
- if (format != AudioSystem::INVALID_FORMAT) {
- if (target == "Manager") {
- mTestFormat = format;
- } else if (mTestOutputs[mCurOutput] != 0) {
- AudioParameter outputParam = AudioParameter();
- outputParam.addInt(String8("format"), format);
- mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
- }
- }
- }
- if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_channels"));
- int channels = 0;
-
- if (value == "Channels Stereo") {
- channels = AudioSystem::CHANNEL_OUT_STEREO;
- } else if (value == "Channels Mono") {
- channels = AudioSystem::CHANNEL_OUT_MONO;
- }
- if (channels != 0) {
- if (target == "Manager") {
- mTestChannels = channels;
- } else if (mTestOutputs[mCurOutput] != 0) {
- AudioParameter outputParam = AudioParameter();
- outputParam.addInt(String8("channels"), channels);
- mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
- }
- }
- }
- if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_sampleRate"));
- if (valueInt >= 0 && valueInt <= 96000) {
- int samplingRate = valueInt;
- if (target == "Manager") {
- mTestSamplingRate = samplingRate;
- } else if (mTestOutputs[mCurOutput] != 0) {
- AudioParameter outputParam = AudioParameter();
- outputParam.addInt(String8("sampling_rate"), samplingRate);
- mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
- }
- }
- }
-
- if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
- param.remove(String8("test_cmd_policy_reopen"));
-
- mpClientInterface->closeOutput(mHardwareOutput);
- delete mOutputs.valueFor(mHardwareOutput);
- mOutputs.removeItem(mHardwareOutput);
-
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
- mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
- if (mHardwareOutput == 0) {
- LOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d",
- outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
- } else {
- AudioParameter outputCmd = AudioParameter();
- outputCmd.addInt(String8("set_id"), 0);
- mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
- addOutput(mHardwareOutput, outputDesc);
- }
- }
-
-
- mpClientInterface->setParameters(0, String8("test_cmd_policy="));
- }
- }
- return false;
-}
-
-void AudioPolicyManagerBase::exit()
-{
- {
- AutoMutex _l(mLock);
- requestExit();
- mWaitWorkCV.signal();
- }
- requestExitAndWait();
-}
-
-int AudioPolicyManagerBase::testOutputIndex(audio_io_handle_t output)
-{
- for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
- if (output == mTestOutputs[i]) return i;
- }
- return 0;
-}
-#endif //AUDIO_POLICY_TEST
-
-// ---
-
-void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc)
-{
- outputDesc->mId = id;
- mOutputs.add(id, outputDesc);
-}
-
-
-#ifdef WITH_A2DP
-status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices device,
- const char *device_address)
-{
- // when an A2DP device is connected, open an A2DP and a duplicated output
- LOGV("opening A2DP output for device %s", device_address);
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
- outputDesc->mDevice = device;
- mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
- 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()) {
- mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput);
- }
- if (mDuplicatedOutput != 0 ||
- !a2dpUsedForSonification()) {
- // If both A2DP and duplicated outputs are open, send device address to A2DP hardware
- // interface
- AudioParameter param;
- param.add(String8("a2dp_sink_address"), String8(device_address));
- mpClientInterface->setParameters(mA2dpOutput, param.toString());
- mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
-
- if (a2dpUsedForSonification()) {
- // add duplicated output descriptor
- AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor();
- dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput);
- dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput);
- dupOutputDesc->mSamplingRate = outputDesc->mSamplingRate;
- dupOutputDesc->mFormat = outputDesc->mFormat;
- dupOutputDesc->mChannels = outputDesc->mChannels;
- dupOutputDesc->mLatency = outputDesc->mLatency;
- addOutput(mDuplicatedOutput, dupOutputDesc);
- applyStreamVolumes(mDuplicatedOutput, device);
- }
- } else {
- LOGW("getOutput() could not open duplicated output for %d and %d",
- mHardwareOutput, mA2dpOutput);
- mpClientInterface->closeOutput(mA2dpOutput);
- mOutputs.removeItem(mA2dpOutput);
- mA2dpOutput = 0;
- delete outputDesc;
- return NO_INIT;
- }
- } else {
- LOGW("setDeviceConnectionState() could not open A2DP output for device %x", device);
- delete outputDesc;
- return NO_INIT;
- }
- AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
-
- if (!a2dpUsedForSonification()) {
- // mute music on A2DP output if a notification or ringtone is playing
- uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
- for (uint32_t i = 0; i < refCount; i++) {
- setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
- }
- }
-
- mA2dpSuspended = false;
-
- return NO_ERROR;
-}
-
-status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device,
- const char *device_address)
-{
- if (mA2dpOutput == 0) {
- LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!");
- return INVALID_OPERATION;
- }
-
- if (mA2dpDeviceAddress != device_address) {
- LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address);
- return INVALID_OPERATION;
- }
-
- // mute media strategy to avoid outputting sound on hardware output while music stream
- // is switched from A2DP output and before music is paused by music application
- setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput);
- setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS);
-
- if (!a2dpUsedForSonification()) {
- // unmute music on A2DP output if a notification or ringtone is playing
- uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION);
- for (uint32_t i = 0; i < refCount; i++) {
- setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput);
- }
- }
- mA2dpDeviceAddress = "";
- mA2dpSuspended = false;
- return NO_ERROR;
-}
-
-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);
- mDuplicatedOutput = 0;
- }
- if (mA2dpOutput != 0) {
- AudioParameter param;
- param.add(String8("closing"), String8("true"));
- mpClientInterface->setParameters(mA2dpOutput, param.toString());
-
- mpClientInterface->closeOutput(mA2dpOutput);
- delete mOutputs.valueFor(mA2dpOutput);
- mOutputs.removeItem(mA2dpOutput);
- mA2dpOutput = 0;
- }
-}
-
-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));
- 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 from duplicated", strategy);
- srcOutput = mDuplicatedOutput;
- } else {
- LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy);
- srcOutput = mA2dpOutput;
- }
- }
- if (a2dpIsUsed && !a2dpWasUsed) {
- bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2);
- srcOutput = mHardwareOutput;
- if (dupUsed) {
- LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy);
- dstOutput = mDuplicatedOutput;
- } else {
- 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, dstOutput);
- }
- }
- }
-}
-
-void AudioPolicyManagerBase::checkOutputForAllStrategies()
-{
- checkOutputForStrategy(STRATEGY_PHONE);
- checkOutputForStrategy(STRATEGY_SONIFICATION);
- checkOutputForStrategy(STRATEGY_MEDIA);
- checkOutputForStrategy(STRATEGY_DTMF);
-}
-
-void AudioPolicyManagerBase::checkA2dpSuspend()
-{
- // suspend A2DP output if:
- // (NOT already suspended) &&
- // ((SCO device is connected &&
- // (forced usage for communication || for record is SCO))) ||
- // (phone state is ringing || in call)
- //
- // restore A2DP output if:
- // (Already suspended) &&
- // ((SCO device is NOT connected ||
- // (forced usage NOT for communication && NOT for record is SCO))) &&
- // (phone state is NOT ringing && NOT in call)
- //
- if (mA2dpOutput == 0) {
- return;
- }
-
- if (mA2dpSuspended) {
- if (((mScoDeviceAddress == "") ||
- ((mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO) &&
- (mForceUse[AudioSystem::FOR_RECORD] != AudioSystem::FORCE_BT_SCO))) &&
- ((mPhoneState != AudioSystem::MODE_IN_CALL) &&
- (mPhoneState != AudioSystem::MODE_RINGTONE))) {
-
- mpClientInterface->restoreOutput(mA2dpOutput);
- mA2dpSuspended = false;
- }
- } else {
- if (((mScoDeviceAddress != "") &&
- ((mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
- (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO))) ||
- ((mPhoneState == AudioSystem::MODE_IN_CALL) ||
- (mPhoneState == AudioSystem::MODE_RINGTONE))) {
-
- mpClientInterface->suspendOutput(mA2dpOutput);
- mA2dpSuspended = true;
- }
- }
-}
-
-
-#endif
-
-uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
-{
- uint32_t device = 0;
-
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
- // check the following by order of priority to request a routing change if necessary:
- // 1: we are in call or the strategy phone is active on the hardware output:
- // use device for strategy phone
- // 2: the strategy sonification is active on the hardware output:
- // use device for strategy sonification
- // 3: the strategy media is active on the hardware output:
- // use device for strategy media
- // 4: the strategy DTMF is active on the hardware output:
- // use device for strategy DTMF
- if (isInCall() ||
- outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
- device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
- device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
- device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
- device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
- }
-
- LOGV("getNewDevice() selected device %x", device);
- return device;
-}
-
-uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type stream) {
- return (uint32_t)getStrategy(stream);
-}
-
-uint32_t AudioPolicyManagerBase::getDevicesForStream(AudioSystem::stream_type stream) {
- uint32_t devices;
- // By checking the range of stream before calling getStrategy, we avoid
- // getStrategy's behavior for invalid streams. getStrategy would do a LOGE
- // and then return STRATEGY_MEDIA, but we want to return the empty set.
- if (stream < (AudioSystem::stream_type) 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
- devices = 0;
- } else {
- AudioPolicyManagerBase::routing_strategy strategy = getStrategy(stream);
- devices = getDeviceForStrategy(strategy, true);
- }
- return devices;
-}
-
-AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(
- AudioSystem::stream_type stream) {
- // stream to strategy mapping
- switch (stream) {
- case AudioSystem::VOICE_CALL:
- case AudioSystem::BLUETOOTH_SCO:
- return STRATEGY_PHONE;
- case AudioSystem::RING:
- case AudioSystem::NOTIFICATION:
- case AudioSystem::ALARM:
- case AudioSystem::ENFORCED_AUDIBLE:
- return STRATEGY_SONIFICATION;
- case AudioSystem::DTMF:
- return STRATEGY_DTMF;
- default:
- LOGE("unknown stream type");
- case AudioSystem::SYSTEM:
- // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
- // while key clicks are played produces a poor result
- case AudioSystem::TTS:
- case AudioSystem::MUSIC:
- return STRATEGY_MEDIA;
- }
-}
-
-uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
-{
- uint32_t device = 0;
-
- if (fromCache) {
- LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
- return mDeviceForStrategy[strategy];
- }
-
- switch (strategy) {
- case STRATEGY_DTMF:
- if (!isInCall()) {
- // when off call, DTMF strategy follows the same rules as MEDIA strategy
- device = getDeviceForStrategy(STRATEGY_MEDIA, false);
- break;
- }
- // when in call, DTMF and PHONE strategies follow the same rules
- // FALL THROUGH
-
- case STRATEGY_PHONE:
- // for phone strategy, we first consider the forced use and then the available devices by order
- // of priority
- switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
- case AudioSystem::FORCE_BT_SCO:
- if (!isInCall() || strategy != STRATEGY_DTMF) {
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
- if (device) break;
- }
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
- if (device) break;
- // if SCO device is requested but no SCO device is available, fall back to default case
- // FALL THROUGH
-
- default: // FORCE_NONE
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
- if (device) break;
-#ifdef WITH_A2DP
- // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
- if (!isInCall() && !mA2dpSuspended) {
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
- if (device) break;
- }
-#endif
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
- if (device == 0) {
- LOGE("getDeviceForStrategy() earpiece device not found");
- }
- break;
-
- case AudioSystem::FORCE_SPEAKER:
-#ifdef WITH_A2DP
- // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
- // A2DP speaker when forcing to speaker output
- if (!isInCall() && !mA2dpSuspended) {
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
- if (device) break;
- }
-#endif
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
- if (device) break;
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
- if (device == 0) {
- LOGE("getDeviceForStrategy() speaker device not found");
- }
- break;
- }
- break;
-
- case STRATEGY_SONIFICATION:
-
- // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by
- // handleIncallSonification().
- if (isInCall()) {
- device = getDeviceForStrategy(STRATEGY_PHONE, false);
- break;
- }
- device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
- if (device == 0) {
- LOGE("getDeviceForStrategy() speaker device not found");
- }
- // The second device used for sonification is the same as the device used by media strategy
- // FALL THROUGH
-
- case STRATEGY_MEDIA: {
- uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
- }
-#ifdef WITH_A2DP
- if ((mA2dpOutput != 0) && !mA2dpSuspended &&
- (strategy != STRATEGY_SONIFICATION || a2dpUsedForSonification())) {
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
- }
- }
-#endif
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
- }
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
- }
-
- // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
- device |= device2;
- if (device == 0) {
- LOGE("getDeviceForStrategy() speaker device not found");
- }
- } break;
-
- default:
- LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
- break;
- }
-
- LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
- return device;
-}
-
-void AudioPolicyManagerBase::updateDeviceForStrategy()
-{
- for (int i = 0; i < NUM_STRATEGIES; i++) {
- mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
- }
-}
-
-void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
-{
- LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
-
-
- if (outputDesc->isDuplicated()) {
- setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
- setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
- return;
- }
-#ifdef WITH_A2DP
- // filter devices according to output selected
- if (output == mA2dpOutput) {
- device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
- } else {
- device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
- }
-#endif
-
- uint32_t prevDevice = (uint32_t)outputDesc->device();
- // Do not change the routing if:
- // - the requestede device is 0
- // - the requested device is the same as current device and force is not specified.
- // Doing this check here allows the caller to call setOutputDevice() without conditions
- if ((device == 0 || device == prevDevice) && !force) {
- LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output);
- return;
- }
-
- outputDesc->mDevice = device;
- // mute media streams if both speaker and headset are selected
- if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {
- setStrategyMute(STRATEGY_MEDIA, true, output);
- // wait for the PCM output buffers to empty before proceeding with the rest of the command
- usleep(outputDesc->mLatency*2*1000);
- }
-
- // do the routing
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)device);
- mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
- // update stream volumes according to new device
- applyStreamVolumes(output, device, delayMs);
-
- // if changing from a combined headset + speaker route, unmute media streams
- if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
- setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
- }
-}
-
-uint32_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource)
-{
- uint32_t device;
-
- switch(inputSource) {
- case AUDIO_SOURCE_DEFAULT:
- case AUDIO_SOURCE_MIC:
- case AUDIO_SOURCE_VOICE_RECOGNITION:
- case AUDIO_SOURCE_VOICE_COMMUNICATION:
- if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
- mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
- device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- } else if (mAvailableInputDevices & AudioSystem::DEVICE_IN_WIRED_HEADSET) {
- device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
- } else {
- device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
- }
- break;
- case AUDIO_SOURCE_CAMCORDER:
- if (hasBackMicrophone()) {
- device = AudioSystem::DEVICE_IN_BACK_MIC;
- } else {
- device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
- }
- break;
- case AUDIO_SOURCE_VOICE_UPLINK:
- case AUDIO_SOURCE_VOICE_DOWNLINK:
- case AUDIO_SOURCE_VOICE_CALL:
- device = AudioSystem::DEVICE_IN_VOICE_CALL;
- break;
- default:
- LOGW("getInput() invalid input source %d", inputSource);
- device = 0;
- break;
- }
- LOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
- return device;
-}
-
-audio_io_handle_t AudioPolicyManagerBase::getActiveInput()
-{
- for (size_t i = 0; i < mInputs.size(); i++) {
- if (mInputs.valueAt(i)->mRefCount > 0) {
- return mInputs.keyAt(i);
- }
- }
- return 0;
-}
-
-float AudioPolicyManagerBase::volIndexToAmpl(uint32_t device, const StreamDescriptor& streamDesc,
- int indexInUi) {
- // the volume index in the UI is relative to the min and max volume indices for this stream type
- int nbSteps = 1 + streamDesc.mVolIndex[StreamDescriptor::VOLMAX] -
- streamDesc.mVolIndex[StreamDescriptor::VOLMIN];
- int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) /
- (streamDesc.mIndexMax - streamDesc.mIndexMin);
-
- // find what part of the curve this index volume belongs to, or if it's out of bounds
- int segment = 0;
- if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLMIN]) { // out of bounds
- return 0.0f;
- } else if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLKNEE1]) {
- segment = 0;
- } else if (volIdx < streamDesc.mVolIndex[StreamDescriptor::VOLKNEE2]) {
- segment = 1;
- } else if (volIdx <= streamDesc.mVolIndex[StreamDescriptor::VOLMAX]) {
- segment = 2;
- } else { // out of bounds
- return 1.0f;
- }
-
- // linear interpolation in the attenuation table in dB
- float decibels = streamDesc.mVolDbAtt[segment] +
- ((float)(volIdx - streamDesc.mVolIndex[segment])) *
- ( (streamDesc.mVolDbAtt[segment+1] - streamDesc.mVolDbAtt[segment]) /
- ((float)(streamDesc.mVolIndex[segment+1] - streamDesc.mVolIndex[segment])) );
-
- float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
-
- LOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f",
- streamDesc.mVolIndex[segment], volIdx, streamDesc.mVolIndex[segment+1],
- streamDesc.mVolDbAtt[segment], decibels, streamDesc.mVolDbAtt[segment+1],
- amplification);
-
- return amplification;
-}
-
-void AudioPolicyManagerBase::initializeVolumeCurves() {
- // initialize the volume curves to a (-49.5 - 0 dB) attenuation in 0.5dB steps
- for (int i=0 ; i< AudioSystem::NUM_STREAM_TYPES ; i++) {
- mStreams[i].mVolIndex[StreamDescriptor::VOLMIN] = 1;
- mStreams[i].mVolDbAtt[StreamDescriptor::VOLMIN] = -49.5f;
- mStreams[i].mVolIndex[StreamDescriptor::VOLKNEE1] = 33;
- mStreams[i].mVolDbAtt[StreamDescriptor::VOLKNEE1] = -33.5f;
- mStreams[i].mVolIndex[StreamDescriptor::VOLKNEE2] = 66;
- mStreams[i].mVolDbAtt[StreamDescriptor::VOLKNEE2] = -17.0f;
- // here we use 100 steps to avoid rounding errors
- // when computing the volume in volIndexToAmpl()
- mStreams[i].mVolIndex[StreamDescriptor::VOLMAX] = 100;
- mStreams[i].mVolDbAtt[StreamDescriptor::VOLMAX] = 0.0f;
- }
-
- // Modification for music: more attenuation for lower volumes, finer steps at high volumes
- mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLMIN] = 1;
- mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLMIN] = -58.0f;
- mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLKNEE1] = 20;
- mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLKNEE1] = -40.0f;
- mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLKNEE2] = 60;
- mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLKNEE2] = -17.0f;
- mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLMAX] = 100;
- mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLMAX] = 0.0f;
-}
-
-float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
-{
- float volume = 1.0;
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
- StreamDescriptor &streamDesc = mStreams[stream];
-
- if (device == 0) {
- device = outputDesc->device();
- }
-
- // if volume is not 0 (not muted), force media volume to max on digital output
- if (stream == AudioSystem::MUSIC &&
- index != mStreams[stream].mIndexMin &&
- device == AudioSystem::DEVICE_OUT_AUX_DIGITAL) {
- return 1.0;
- }
-
- volume = volIndexToAmpl(device, streamDesc, index);
-
- // if a headset is connected, apply the following rules to ring tones and notifications
- // to avoid sound level bursts in user's ears:
- // - always attenuate ring tones and notifications volume by 6dB
- // - if music is playing, always limit the volume to current music volume,
- // with a minimum threshold at -36dB so that notification is always perceived.
- if ((device &
- (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
- AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
- AudioSystem::DEVICE_OUT_WIRED_HEADSET |
- AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
- ((getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) ||
- (stream == AudioSystem::SYSTEM)) &&
- streamDesc.mCanBeMuted) {
- volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
- // when the phone is ringing we must consider that music could have been paused just before
- // by the music application and behave as if music was active if the last music track was
- // just stopped
- if (outputDesc->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) {
- float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device);
- float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
- if (volume > minVol) {
- volume = minVol;
- LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
- }
- }
- }
-
- return volume;
-}
-
-status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
-{
-
- // do not change actual stream volume if the stream is muted
- if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
- LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]);
- return NO_ERROR;
- }
-
- // do not change in call volume if bluetooth is connected and vice versa
- if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
- (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
- LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
- stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
- return INVALID_OPERATION;
- }
-
- float volume = computeVolume(stream, index, output, device);
- // We actually change the volume if:
- // - the float value returned by computeVolume() changed
- // - the force flag is set
- if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
- force) {
- mOutputs.valueFor(output)->mCurVolume[stream] = volume;
- LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
- if (stream == AudioSystem::VOICE_CALL ||
- stream == AudioSystem::DTMF ||
- stream == AudioSystem::BLUETOOTH_SCO) {
- // offset value to reflect actual hardware volume that never reaches 0
- // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
- volume = 0.01 + 0.99 * volume;
- // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
- // enabled
- if (stream == AudioSystem::BLUETOOTH_SCO) {
- mpClientInterface->setStreamVolume(AudioSystem::VOICE_CALL, volume, output, delayMs);
- }
- }
-
- mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
- }
-
- if (stream == AudioSystem::VOICE_CALL ||
- stream == AudioSystem::BLUETOOTH_SCO) {
- float voiceVolume;
- // Force voice volume to max for bluetooth SCO as volume is managed by the headset
- if (stream == AudioSystem::VOICE_CALL) {
- voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
- } else {
- voiceVolume = 1.0;
- }
-
- if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
- mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
- mLastVoiceVolume = voiceVolume;
- }
- }
-
- return NO_ERROR;
-}
-
-void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs, bool force)
-{
- LOGV("applyStreamVolumes() for output %d and device %x", output, device);
-
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs, force);
- }
-}
-
-void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs)
-{
- LOGV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
- for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- if (getStrategy((AudioSystem::stream_type)stream) == strategy) {
- setStreamMute(stream, on, output, delayMs);
- }
- }
-}
-
-void AudioPolicyManagerBase::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs)
-{
- StreamDescriptor &streamDesc = mStreams[stream];
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
-
- LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, outputDesc->mMuteCount[stream]);
-
- if (on) {
- if (outputDesc->mMuteCount[stream] == 0) {
- if (streamDesc.mCanBeMuted) {
- checkAndSetVolume(stream, 0, output, outputDesc->device(), delayMs);
- }
- }
- // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
- outputDesc->mMuteCount[stream]++;
- } else {
- if (outputDesc->mMuteCount[stream] == 0) {
- LOGW("setStreamMute() unmuting non muted stream!");
- return;
- }
- if (--outputDesc->mMuteCount[stream] == 0) {
- checkAndSetVolume(stream, streamDesc.mIndexCur, output, outputDesc->device(), delayMs);
- }
- }
-}
-
-void AudioPolicyManagerBase::handleIncallSonification(int stream, bool starting, bool stateChange)
-{
- // if the stream pertains to sonification strategy and we are in call we must
- // mute the stream if it is low visibility. If it is high visibility, we must play a tone
- // in the device used for phone strategy and play the tone if the selected device does not
- // interfere with the device used for phone strategy
- // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as
- // many times as there are active tracks on the output
-
- if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) {
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput);
- LOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
- stream, starting, outputDesc->mDevice, stateChange);
- if (outputDesc->mRefCount[stream]) {
- int muteCount = 1;
- if (stateChange) {
- muteCount = outputDesc->mRefCount[stream];
- }
- if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) {
- LOGV("handleIncallSonification() low visibility, muteCount %d", muteCount);
- for (int i = 0; i < muteCount; i++) {
- setStreamMute(stream, starting, mHardwareOutput);
- }
- } else {
- LOGV("handleIncallSonification() high visibility");
- if (outputDesc->device() & getDeviceForStrategy(STRATEGY_PHONE)) {
- LOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount);
- for (int i = 0; i < muteCount; i++) {
- setStreamMute(stream, starting, mHardwareOutput);
- }
- }
- if (starting) {
- mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL);
- } else {
- mpClientInterface->stopTone();
- }
- }
- }
- }
-}
-
-bool AudioPolicyManagerBase::isInCall()
-{
- return isStateInCall(mPhoneState);
-}
-
-bool AudioPolicyManagerBase::isStateInCall(int state) {
- return ((state == AudioSystem::MODE_IN_CALL) ||
- (state == AudioSystem::MODE_IN_COMMUNICATION));
-}
-
-bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream,
- uint32_t samplingRate,
- uint32_t format,
- uint32_t channels,
- AudioSystem::output_flags flags,
- uint32_t device)
-{
- return ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
- (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()
- : mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
- mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0)
-{
- // clear usage count for all stream types
- for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
- mRefCount[i] = 0;
- mCurVolume[i] = -1.0;
- mMuteCount[i] = 0;
- mStopTime[i] = 0;
- }
-}
-
-uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::device()
-{
- uint32_t device = 0;
- if (isDuplicated()) {
- device = mOutput1->mDevice | mOutput2->mDevice;
- } else {
- device = mDevice;
- }
- return device;
-}
-
-void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta)
-{
- // forward usage count change to attached outputs
- if (isDuplicated()) {
- mOutput1->changeRefCount(stream, delta);
- mOutput2->changeRefCount(stream, delta);
- }
- if ((delta + (int)mRefCount[stream]) < 0) {
- LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
- mRefCount[stream] = 0;
- return;
- }
- mRefCount[stream] += delta;
- LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
-}
-
-uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::refCount()
-{
- uint32_t refcount = 0;
- for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
- refcount += mRefCount[i];
- }
- return refcount;
-}
-
-uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::strategyRefCount(routing_strategy strategy)
-{
- uint32_t refCount = 0;
- for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
- if (getStrategy((AudioSystem::stream_type)i) == strategy) {
- refCount += mRefCount[i];
- }
- }
- return refCount;
-}
-
-status_t AudioPolicyManagerBase::AudioOutputDescriptor::dump(int fd)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
- result.append(buffer);
- snprintf(buffer, SIZE, " Format: %d\n", mFormat);
- result.append(buffer);
- snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
- result.append(buffer);
- snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
- result.append(buffer);
- snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
- result.append(buffer);
- snprintf(buffer, SIZE, " Devices %08x\n", device());
- result.append(buffer);
- snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
- result.append(buffer);
- for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
- snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", i, mCurVolume[i], mRefCount[i], mMuteCount[i]);
- result.append(buffer);
- }
- write(fd, result.string(), result.size());
-
- return NO_ERROR;
-}
-
-// --- AudioInputDescriptor class implementation
-
-AudioPolicyManagerBase::AudioInputDescriptor::AudioInputDescriptor()
- : mSamplingRate(0), mFormat(0), mChannels(0),
- mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
-{
-}
-
-status_t AudioPolicyManagerBase::AudioInputDescriptor::dump(int fd)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
- result.append(buffer);
- snprintf(buffer, SIZE, " Format: %d\n", mFormat);
- result.append(buffer);
- snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
- result.append(buffer);
- snprintf(buffer, SIZE, " Acoustics %08x\n", mAcoustics);
- result.append(buffer);
- snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
- result.append(buffer);
- snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount);
- result.append(buffer);
- write(fd, result.string(), result.size());
-
- return NO_ERROR;
-}
-
-// --- StreamDescriptor class implementation
-
-void AudioPolicyManagerBase::StreamDescriptor::dump(char* buffer, size_t size)
-{
- snprintf(buffer, size, " %02d %02d %02d %d\n",
- mIndexMin,
- mIndexMax,
- mIndexCur,
- 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 b614c48..eebc1b3 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -30,11 +30,15 @@
#include <utils/String16.h>
#include <utils/threads.h>
#include "AudioPolicyService.h"
-#include <hardware_legacy/AudioPolicyManagerBase.h>
#include <cutils/properties.h>
#include <dlfcn.h>
#include <hardware_legacy/power.h>
+#include <hardware/hardware.h>
+#include <hardware/audio.h>
+#include <hardware/audio_policy.h>
+#include <hardware/audio_policy_hal.h>
+
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
@@ -44,7 +48,6 @@
namespace android {
-
static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n";
static const char *kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n";
@@ -61,12 +64,19 @@ static bool checkPermission() {
return ok;
}
+namespace {
+ extern struct audio_policy_service_ops aps_ops;
+};
+
// ----------------------------------------------------------------------------
AudioPolicyService::AudioPolicyService()
- : BnAudioPolicyService() , mpPolicyManager(NULL)
+ : BnAudioPolicyService() , mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL)
{
char value[PROPERTY_VALUE_MAX];
+ const struct hw_module_t *module;
+ int forced_val;
+ int rc;
Mutex::Autolock _l(mLock);
@@ -75,33 +85,32 @@ AudioPolicyService::AudioPolicyService()
// start audio commands thread
mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
-#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
- mpPolicyManager = new AudioPolicyManagerBase(this);
- LOGV("build for GENERIC_AUDIO - using generic audio policy");
-#else
- // if running in emulation - use the emulator driver
- if (property_get("ro.kernel.qemu", value, 0)) {
- LOGV("Running in emulation - using generic audio policy");
- mpPolicyManager = new AudioPolicyManagerBase(this);
- }
- else {
- LOGV("Using hardware specific audio policy");
- mpPolicyManager = createAudioPolicyManager(this);
- }
-#endif
+ /* instantiate the audio policy manager */
+ rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
+ if (rc)
+ return;
- if ((mpPolicyManager != NULL) && (mpPolicyManager->initCheck() != NO_ERROR)) {
- delete mpPolicyManager;
- mpPolicyManager = NULL;
- }
+ rc = audio_policy_dev_open(module, &mpAudioPolicyDev);
+ LOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc));
+ if (rc)
+ return;
- if (mpPolicyManager == NULL) {
- LOGE("Could not create AudioPolicyManager");
- } else {
- // load properties
- property_get("ro.camera.sound.forced", value, "0");
- mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
- }
+ rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
+ &mpAudioPolicy);
+ LOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc));
+ if (rc)
+ return;
+
+ rc = mpAudioPolicy->init_check(mpAudioPolicy);
+ LOGE_IF(rc, "couldn't init_check the audio policy (%s)", strerror(-rc));
+ if (rc)
+ return;
+
+ property_get("ro.camera.sound.forced", value, "0");
+ forced_val = strtol(value, NULL, 0);
+ mpAudioPolicy->set_can_mute_enforced_audible(mpAudioPolicy, !forced_val);
+
+ LOGI("Loaded audio policy from %s (%s)", module->name, module->id);
}
AudioPolicyService::~AudioPolicyService()
@@ -111,57 +120,59 @@ AudioPolicyService::~AudioPolicyService()
mAudioCommandThread->exit();
mAudioCommandThread.clear();
- if (mpPolicyManager) {
- delete mpPolicyManager;
- }
+ if (mpAudioPolicy && mpAudioPolicyDev)
+ mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy);
+ if (mpAudioPolicyDev)
+ audio_policy_dev_close(mpAudioPolicyDev);
}
-
-status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
+status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
+ audio_policy_dev_state_t state,
const char *device_address)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (!AudioSystem::isOutputDevice(device) && !AudioSystem::isInputDevice(device)) {
+ if (!audio_is_output_device(device) && !audio_is_input_device(device)) {
return BAD_VALUE;
}
- if (state != AudioSystem::DEVICE_STATE_AVAILABLE &&
- state != AudioSystem::DEVICE_STATE_UNAVAILABLE) {
+ if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE &&
+ state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
return BAD_VALUE;
}
LOGV("setDeviceConnectionState() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->setDeviceConnectionState(device, state, device_address);
+ return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device,
+ state, device_address);
}
-AudioSystem::device_connection_state AudioPolicyService::getDeviceConnectionState(
- AudioSystem::audio_devices device,
+audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState(
+ audio_devices_t device,
const char *device_address)
{
- if (mpPolicyManager == NULL) {
- return AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ if (mpAudioPolicy == NULL) {
+ return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
}
if (!checkPermission()) {
- return AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
}
- return mpPolicyManager->getDeviceConnectionState(device, device_address);
+ return mpAudioPolicy->get_device_connection_state(mpAudioPolicy, device,
+ device_address);
}
status_t AudioPolicyService::setPhoneState(int state)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (state < 0 || state >= AudioSystem::NUM_MODES) {
+ if (state < 0 || state >= AUDIO_MODE_CNT) {
return BAD_VALUE;
}
@@ -171,215 +182,215 @@ status_t AudioPolicyService::setPhoneState(int state)
AudioSystem::setMode(state);
Mutex::Autolock _l(mLock);
- mpPolicyManager->setPhoneState(state);
+ mpAudioPolicy->set_phone_state(mpAudioPolicy, state);
return NO_ERROR;
}
status_t AudioPolicyService::setRingerMode(uint32_t mode, uint32_t mask)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- mpPolicyManager->setRingerMode(mode, mask);
+ mpAudioPolicy->set_ringer_mode(mpAudioPolicy, mode, mask);
return NO_ERROR;
}
-status_t AudioPolicyService::setForceUse(AudioSystem::force_use usage,
- AudioSystem::forced_config config)
+status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,
+ audio_policy_forced_cfg_t config)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) {
+ if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
return BAD_VALUE;
}
- if (config < 0 || config >= AudioSystem::NUM_FORCE_CONFIG) {
+ if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) {
return BAD_VALUE;
}
LOGV("setForceUse() tid %d", gettid());
Mutex::Autolock _l(mLock);
- mpPolicyManager->setForceUse(usage, config);
+ mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config);
return NO_ERROR;
}
-AudioSystem::forced_config AudioPolicyService::getForceUse(AudioSystem::force_use usage)
+audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage)
{
- if (mpPolicyManager == NULL) {
- return AudioSystem::FORCE_NONE;
+ if (mpAudioPolicy == NULL) {
+ return AUDIO_POLICY_FORCE_NONE;
}
if (!checkPermission()) {
- return AudioSystem::FORCE_NONE;
+ return AUDIO_POLICY_FORCE_NONE;
}
- if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) {
- return AudioSystem::FORCE_NONE;
+ if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
+ return AUDIO_POLICY_FORCE_NONE;
}
- return mpPolicyManager->getForceUse(usage);
+ return mpAudioPolicy->get_force_use(mpAudioPolicy, usage);
}
-audio_io_handle_t AudioPolicyService::getOutput(AudioSystem::stream_type stream,
+audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
- AudioSystem::output_flags flags)
+ audio_policy_output_flags_t flags)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
LOGV("getOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->getOutput(stream, samplingRate, format, channels, flags);
+ return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channels, flags);
}
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
LOGV("startOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->startOutput(output, stream, session);
+ return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session);
}
status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
LOGV("stopOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpPolicyManager->stopOutput(output, stream, session);
+ return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session);
}
void AudioPolicyService::releaseOutput(audio_io_handle_t output)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return;
}
LOGV("releaseOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- mpPolicyManager->releaseOutput(output);
+ mpAudioPolicy->release_output(mpAudioPolicy, output);
}
audio_io_handle_t AudioPolicyService::getInput(int inputSource,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
- AudioSystem::audio_in_acoustics acoustics)
+ audio_in_acoustics_t acoustics)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->getInput(inputSource, samplingRate, format, channels, acoustics);
+ return mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, format, channels, acoustics);
}
status_t AudioPolicyService::startInput(audio_io_handle_t input)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->startInput(input);
+ return mpAudioPolicy->start_input(mpAudioPolicy, input);
}
status_t AudioPolicyService::stopInput(audio_io_handle_t input)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->stopInput(input);
+ return mpAudioPolicy->stop_input(mpAudioPolicy, input);
}
void AudioPolicyService::releaseInput(audio_io_handle_t input)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return;
}
Mutex::Autolock _l(mLock);
- mpPolicyManager->releaseInput(input);
+ mpAudioPolicy->release_input(mpAudioPolicy, input);
}
-status_t AudioPolicyService::initStreamVolume(AudioSystem::stream_type stream,
+status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
- mpPolicyManager->initStreamVolume(stream, indexMin, indexMax);
+ mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax);
return NO_ERROR;
}
-status_t AudioPolicyService::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, int index)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
- return mpPolicyManager->setStreamVolumeIndex(stream, index);
+ return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index);
}
-status_t AudioPolicyService::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, int *index)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
- if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
- return mpPolicyManager->getStreamVolumeIndex(stream, index);
+ return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index);
}
-uint32_t AudioPolicyService::getStrategyForStream(AudioSystem::stream_type stream)
+uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
- return mpPolicyManager->getStrategyForStream(stream);
+ return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream);
}
-uint32_t AudioPolicyService::getDevicesForStream(AudioSystem::stream_type stream)
+uint32_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
- return mpPolicyManager->getDevicesForStream(stream);
+ return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream);
}
audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *desc)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->getOutputForEffect(desc);
+ return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc);
}
status_t AudioPolicyService::registerEffect(effect_descriptor_t *desc,
@@ -388,27 +399,27 @@ status_t AudioPolicyService::registerEffect(effect_descriptor_t *desc,
int session,
int id)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
- return mpPolicyManager->registerEffect(desc, output, strategy, session, id);
+ return mpAudioPolicy->register_effect(mpAudioPolicy, desc, output, strategy, session, id);
}
status_t AudioPolicyService::unregisterEffect(int id)
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return NO_INIT;
}
- return mpPolicyManager->unregisterEffect(id);
+ return mpAudioPolicy->unregister_effect(mpAudioPolicy, id);
}
bool AudioPolicyService::isStreamActive(int stream, uint32_t inPastMs) const
{
- if (mpPolicyManager == NULL) {
+ if (mpAudioPolicy == NULL) {
return 0;
}
Mutex::Autolock _l(mLock);
- return mpPolicyManager->isStreamActive(stream, inPastMs);
+ return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs);
}
void AudioPolicyService::binderDied(const wp<IBinder>& who) {
@@ -435,7 +446,7 @@ status_t AudioPolicyService::dumpInternals(int fd)
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpPolicyManager);
+ snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpAudioPolicy);
result.append(buffer);
snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get());
result.append(buffer);
@@ -465,8 +476,8 @@ status_t AudioPolicyService::dump(int fd, const Vector<String16>& args)
mTonePlaybackThread->dump(fd);
}
- if (mpPolicyManager) {
- mpPolicyManager->dump(fd);
+ if (mpAudioPolicy) {
+ mpAudioPolicy->dump(mpAudioPolicy, fd);
}
if (locked) mLock.unlock();
@@ -495,154 +506,6 @@ status_t AudioPolicyService::onTransact(
}
-// ----------------------------------------------------------------------------
-// AudioPolicyClientInterface implementation
-// ----------------------------------------------------------------------------
-
-
-audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t *pLatencyMs,
- AudioSystem::output_flags flags)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("openOutput() could not get AudioFlinger");
- return 0;
- }
-
- 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)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("openDuplicateOutput() could not get AudioFlinger");
- return 0;
- }
- return af->openDuplicateOutput(output1, output2);
-}
-
-status_t AudioPolicyService::closeOutput(audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
-
- return af->closeOutput(output);
-}
-
-
-status_t AudioPolicyService::suspendOutput(audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("suspendOutput() could not get AudioFlinger");
- return PERMISSION_DENIED;
- }
-
- return af->suspendOutput(output);
-}
-
-status_t AudioPolicyService::restoreOutput(audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("restoreOutput() could not get AudioFlinger");
- return PERMISSION_DENIED;
- }
-
- return af->restoreOutput(output);
-}
-
-audio_io_handle_t AudioPolicyService::openInput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t acoustics)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- LOGW("openInput() could not get AudioFlinger");
- return 0;
- }
-
- return af->openInput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, acoustics);
-}
-
-status_t AudioPolicyService::closeInput(audio_io_handle_t input)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
-
- return af->closeInput(input);
-}
-
-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)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
-
- 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)
-{
- mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs);
-}
-
-String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const String8& keys)
-{
- String8 result = AudioSystem::getParameters(ioHandle, keys);
- return result;
-}
-
-status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone,
- AudioSystem::stream_type stream)
-{
- mTonePlaybackThread->startToneCommand(tone, stream);
- return NO_ERROR;
-}
-
-status_t AudioPolicyService::stopTone()
-{
- mTonePlaybackThread->stopToneCommand();
- return NO_ERROR;
-}
-
-status_t AudioPolicyService::setVoiceVolume(float volume, int delayMs)
-{
- return mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
-}
-
// ----------- AudioPolicyService::AudioCommandThread implementation ----------
AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
@@ -859,7 +722,7 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream,
}
status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
- const String8& keyValuePairs,
+ const char *keyValuePairs,
int delayMs)
{
status_t status = NO_ERROR;
@@ -868,7 +731,7 @@ status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
command->mCommand = SET_PARAMETERS;
ParametersData *data = new ParametersData();
data->mIO = ioHandle;
- data->mKeyValuePairs = keyValuePairs;
+ data->mKeyValuePairs = String8(keyValuePairs);
command->mParam = data;
if (delayMs == 0) {
command->mWaitStatus = true;
@@ -878,7 +741,7 @@ 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);
+ keyValuePairs, ioHandle, delayMs);
mWaitWorkCV.signal();
if (command->mWaitStatus) {
command->mCond.wait(mLock);
@@ -1023,4 +886,226 @@ void AudioPolicyService::AudioCommandThread::AudioCommand::dump(char* buffer, si
mParam);
}
+/******* helpers for the service_ops callbacks defined below *********/
+void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
+ const char *keyValuePairs,
+ int delayMs)
+{
+ mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs,
+ delayMs);
+}
+
+int AudioPolicyService::setStreamVolume(audio_stream_type_t stream,
+ float volume,
+ audio_io_handle_t output,
+ int delayMs)
+{
+ return (int)mAudioCommandThread->volumeCommand((int)stream, volume,
+ (int)output, delayMs);
+}
+
+int AudioPolicyService::startTone(audio_policy_tone_t tone,
+ audio_stream_type_t stream)
+{
+ if (tone != AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION)
+ LOGE("startTone: illegal tone requested (%d)", tone);
+ if (stream != AUDIO_STREAM_VOICE_CALL)
+ LOGE("startTone: illegal stream (%d) requested for tone %d", stream,
+ tone);
+ mTonePlaybackThread->startToneCommand(ToneGenerator::TONE_SUP_CALL_WAITING,
+ AUDIO_STREAM_VOICE_CALL);
+ return 0;
+}
+
+int AudioPolicyService::stopTone()
+{
+ mTonePlaybackThread->stopToneCommand();
+ return 0;
+}
+
+int AudioPolicyService::setVoiceVolume(float volume, int delayMs)
+{
+ return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
+}
+
+/* implementation of the interface to the policy manager */
+extern "C" {
+
+static audio_io_handle_t aps_open_output(void *service,
+ uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ audio_policy_output_flags_t flags)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return 0;
+ }
+
+ return af->openOutput(pDevices, pSamplingRate, pFormat, pChannels,
+ pLatencyMs, flags);
+}
+
+static audio_io_handle_t aps_open_dup_output(void *service,
+ audio_io_handle_t output1,
+ audio_io_handle_t output2)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return 0;
+ }
+ return af->openDuplicateOutput(output1, output2);
+}
+
+static int aps_close_output(void *service, audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL)
+ return PERMISSION_DENIED;
+
+ return af->closeOutput(output);
+}
+
+static int aps_suspend_output(void *service, audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return PERMISSION_DENIED;
+ }
+
+ return af->suspendOutput(output);
+}
+
+static int aps_restore_output(void *service, audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return PERMISSION_DENIED;
+ }
+
+ return af->restoreOutput(output);
+}
+
+static audio_io_handle_t aps_open_input(void *service,
+ uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL) {
+ LOGW("%s: could not get AudioFlinger", __func__);
+ return 0;
+ }
+
+ return af->openInput(pDevices, pSamplingRate, pFormat, pChannels,
+ acoustics);
+}
+
+static int aps_close_input(void *service, audio_io_handle_t input)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL)
+ return PERMISSION_DENIED;
+
+ return af->closeInput(input);
+}
+
+static int aps_set_stream_output(void *service, audio_stream_type_t stream,
+ audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL)
+ return PERMISSION_DENIED;
+
+ return af->setStreamOutput(stream, output);
+}
+
+static int aps_move_effects(void *service, int session,
+ audio_io_handle_t src_output,
+ audio_io_handle_t dst_output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == NULL)
+ return PERMISSION_DENIED;
+
+ return af->moveEffects(session, (int)src_output, (int)dst_output);
+}
+
+static char * aps_get_parameters(void *service, audio_io_handle_t io_handle,
+ const char *keys)
+{
+ String8 result = AudioSystem::getParameters(io_handle, String8(keys));
+ return strdup(result.string());
+}
+
+static void aps_set_parameters(void *service, audio_io_handle_t io_handle,
+ const char *kv_pairs, int delay_ms)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms);
+}
+
+static int aps_set_stream_volume(void *service, audio_stream_type_t stream,
+ float volume, audio_io_handle_t output,
+ int delay_ms)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ return audioPolicyService->setStreamVolume(stream, volume, output,
+ delay_ms);
+}
+
+static int aps_start_tone(void *service, audio_policy_tone_t tone,
+ audio_stream_type_t stream)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ return audioPolicyService->startTone(tone, stream);
+}
+
+static int aps_stop_tone(void *service)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ return audioPolicyService->stopTone();
+}
+
+static int aps_set_voice_volume(void *service, float volume, int delay_ms)
+{
+ AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
+
+ return audioPolicyService->setVoiceVolume(volume, delay_ms);
+}
+
+}; // extern "C"
+
+namespace {
+ struct audio_policy_service_ops aps_ops = {
+ open_output : aps_open_output,
+ open_duplicate_output : aps_open_dup_output,
+ close_output : aps_close_output,
+ suspend_output : aps_suspend_output,
+ restore_output : aps_restore_output,
+ open_input : aps_open_input,
+ close_input : aps_close_input,
+ set_stream_volume : aps_set_stream_volume,
+ set_stream_output : aps_set_stream_output,
+ set_parameters : aps_set_parameters,
+ get_parameters : aps_get_parameters,
+ start_tone : aps_start_tone,
+ stop_tone : aps_stop_tone,
+ set_voice_volume : aps_set_voice_volume,
+ move_effects : aps_move_effects,
+ };
+}; // namespace <unnamed>
+
}; // namespace android
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index faad893..01e592b 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -18,11 +18,14 @@
#define ANDROID_AUDIOPOLICYSERVICE_H
#include <media/IAudioPolicyService.h>
-#include <hardware_legacy/AudioPolicyInterface.h>
#include <media/ToneGenerator.h>
#include <utils/Vector.h>
#include <binder/BinderService.h>
+#include <hardware/audio.h>
+#include <hardware/audio_policy.h>
+#include <hardware/audio_policy_hal.h>
+
namespace android {
class String8;
@@ -32,7 +35,7 @@ class String8;
class AudioPolicyService :
public BinderService<AudioPolicyService>,
public BnAudioPolicyService,
- public AudioPolicyClientInterface,
+// public AudioPolicyClientInterface,
public IBinder::DeathRecipient
{
friend class BinderService<AudioPolicyService>;
@@ -47,46 +50,46 @@ public:
// BnAudioPolicyService (see AudioPolicyInterface for method descriptions)
//
- virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
+ virtual status_t setDeviceConnectionState(audio_devices_t device,
+ audio_policy_dev_state_t state,
const char *device_address);
- virtual AudioSystem::device_connection_state getDeviceConnectionState(
- AudioSystem::audio_devices device,
+ virtual audio_policy_dev_state_t getDeviceConnectionState(
+ audio_devices_t device,
const char *device_address);
virtual status_t setPhoneState(int state);
virtual status_t setRingerMode(uint32_t mode, uint32_t mask);
- virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
- virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage);
- virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
+ virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config);
+ virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage);
+ virtual audio_io_handle_t getOutput(audio_stream_type_t stream,
uint32_t samplingRate = 0,
- uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
- AudioSystem::output_flags flags =
- AudioSystem::OUTPUT_FLAG_INDIRECT);
+ audio_policy_output_flags_t flags =
+ AUDIO_POLICY_OUTPUT_FLAG_INDIRECT);
virtual status_t startOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t stream,
int session = 0);
virtual status_t stopOutput(audio_io_handle_t output,
- AudioSystem::stream_type stream,
+ audio_stream_type_t 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 format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
- AudioSystem::audio_in_acoustics acoustics =
- (AudioSystem::audio_in_acoustics)0);
+ audio_in_acoustics_t acoustics =
+ (audio_in_acoustics_t)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);
- virtual status_t initStreamVolume(AudioSystem::stream_type stream,
+ virtual status_t initStreamVolume(audio_stream_type_t stream,
int indexMin,
int indexMax);
- virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
- virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);
+ virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, int index);
+ virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index);
- virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream);
- virtual uint32_t getDevicesForStream(AudioSystem::stream_type stream);
+ virtual uint32_t getStrategyForStream(audio_stream_type_t stream);
+ virtual uint32_t getDevicesForStream(audio_stream_type_t stream);
virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc);
virtual status_t registerEffect(effect_descriptor_t *desc,
@@ -107,40 +110,21 @@ public:
virtual void binderDied(const wp<IBinder>& who);
//
- // AudioPolicyClientInterface
+ // Helpers for the struct audio_policy_service_ops implementation.
+ // This is used by the audio policy manager for certain operations that
+ // are implemented by the policy service.
//
- virtual audio_io_handle_t openOutput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- 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 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);
- virtual audio_io_handle_t openInput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t acoustics);
- virtual status_t closeInput(audio_io_handle_t input);
- virtual status_t setStreamVolume(AudioSystem::stream_type stream,
+ virtual void setParameters(audio_io_handle_t ioHandle,
+ const char *keyValuePairs,
+ int delayMs);
+
+ virtual status_t setStreamVolume(audio_stream_type_t 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 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 startTone(audio_policy_tone_t tone, audio_stream_type_t 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();
@@ -180,7 +164,7 @@ private:
void startToneCommand(int type = 0, int stream = 0);
void stopToneCommand();
status_t volumeCommand(int stream, float volume, int output, int delayMs = 0);
- status_t parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs = 0);
+ status_t parametersCommand(int ioHandle, const char *keyValuePairs, int delayMs = 0);
status_t voiceVolumeCommand(float volume, int delayMs = 0);
void insertCommand_l(AudioCommand *command, int delayMs = 0);
@@ -240,9 +224,11 @@ private:
mutable Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing
// device connection state or routing
- AudioPolicyInterface* mpPolicyManager; // the platform specific policy manager
sp <AudioCommandThread> mAudioCommandThread; // audio commands thread
sp <AudioCommandThread> mTonePlaybackThread; // tone playback thread
+
+ struct audio_policy_device *mpAudioPolicyDev;
+ struct audio_policy *mpAudioPolicy;
};
}; // namespace android
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index b52fc69..e35435e 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -1,38 +1,5 @@
LOCAL_PATH:= $(call my-dir)
-# Set USE_CAMERA_STUB if you don't want to use the hardware camera.
-
-# force these builds to use camera stub only
-ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
- USE_CAMERA_STUB:=true
-endif
-
-ifeq ($(USE_CAMERA_STUB),)
- USE_CAMERA_STUB:=false
-endif
-
-ifeq ($(USE_CAMERA_STUB),true)
-#
-# libcamerastub
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- CameraHardwareStub.cpp \
- FakeCamera.cpp
-
-LOCAL_MODULE:= libcamerastub
-
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_CFLAGS += -DSINGLE_PROCESS
-endif
-
-LOCAL_SHARED_LIBRARIES:= libui
-
-include $(BUILD_STATIC_LIBRARY)
-endif # USE_CAMERA_STUB
-
#
# libcameraservice
#
@@ -49,19 +16,9 @@ LOCAL_SHARED_LIBRARIES:= \
libcutils \
libmedia \
libcamera_client \
- libsurfaceflinger_client \
- libgui
+ libgui \
+ libhardware
LOCAL_MODULE:= libcameraservice
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_CFLAGS += -DSINGLE_PROCESS
-endif
-
-ifeq ($(USE_CAMERA_STUB), true)
-LOCAL_STATIC_LIBRARIES += libcamerastub
-else
-LOCAL_SHARED_LIBRARIES += libcamera
-endif
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
new file mode 100644
index 0000000..f9fa30e
--- /dev/null
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -0,0 +1,619 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
+#define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
+
+#include <binder/IMemory.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <utils/RefBase.h>
+#include <surfaceflinger/ISurface.h>
+#include <ui/android_native_buffer.h>
+#include <ui/GraphicBuffer.h>
+#include <camera/Camera.h>
+#include <camera/CameraParameters.h>
+#include <system/window.h>
+#include <hardware/camera.h>
+
+namespace android {
+
+typedef void (*notify_callback)(int32_t msgType,
+ int32_t ext1,
+ int32_t ext2,
+ void* user);
+
+typedef void (*data_callback)(int32_t msgType,
+ const sp<IMemory> &dataPtr,
+ void* user);
+
+typedef void (*data_callback_timestamp)(nsecs_t timestamp,
+ int32_t msgType,
+ const sp<IMemory> &dataPtr,
+ void *user);
+
+/**
+ * CameraHardwareInterface.h defines the interface to the
+ * camera hardware abstraction layer, used for setting and getting
+ * parameters, live previewing, and taking pictures.
+ *
+ * It is a referenced counted interface with RefBase as its base class.
+ * CameraService calls openCameraHardware() to retrieve a strong pointer to the
+ * instance of this interface and may be called multiple times. The
+ * following steps describe a typical sequence:
+ *
+ * -# After CameraService calls openCameraHardware(), getParameters() and
+ * setParameters() are used to initialize the camera instance.
+ * CameraService calls getPreviewHeap() to establish access to the
+ * preview heap so it can be registered with SurfaceFlinger for
+ * efficient display updating while in preview mode.
+ * -# startPreview() is called. The camera instance then periodically
+ * sends the message CAMERA_MSG_PREVIEW_FRAME (if enabled) each time
+ * a new preview frame is available. If data callback code needs to use
+ * this memory after returning, it must copy the data.
+ *
+ * Prior to taking a picture, CameraService calls autofocus(). When auto
+ * focusing has completed, the camera instance sends a CAMERA_MSG_FOCUS notification,
+ * which informs the application whether focusing was successful. The camera instance
+ * only sends this message once and it is up to the application to call autoFocus()
+ * again if refocusing is desired.
+ *
+ * CameraService calls takePicture() to request the camera instance take a
+ * picture. At this point, if a shutter, postview, raw, and/or compressed callback
+ * is desired, the corresponding message must be enabled. As with CAMERA_MSG_PREVIEW_FRAME,
+ * any memory provided in a data callback must be copied if it's needed after returning.
+ */
+
+class CameraHardwareInterface : public virtual RefBase {
+public:
+ CameraHardwareInterface(hw_module_t *module, const char *name)
+ {
+ mDevice = 0;
+ mName = name;
+ LOGI("Opening camera %s, this %p", name, this);
+ int rc = module->methods->open(module, name,
+ (hw_device_t **)&mDevice);
+ if (rc != OK)
+ LOGE("Could not open camera %s: %d", name, rc);
+ initHalPreviewWindow();
+ }
+
+ ~CameraHardwareInterface()
+ {
+ LOGI("Destroying camera %s", mName.string());
+ int rc = mDevice->common.close(&mDevice->common);
+ if (rc != OK)
+ LOGE("Could not close camera %s: %d", mName.string(), rc);
+ }
+
+ /** Set the ANativeWindow to which preview frames are sent */
+ status_t setPreviewWindow(const sp<ANativeWindow>& buf)
+ {
+ LOGV("%s(%s) buf %p", __FUNCTION__, mName.string(), buf.get());
+
+ if (mDevice->ops->set_preview_window) {
+ mPreviewWindow = buf;
+ mHalPreviewWindow.user = this;
+ LOGV("%s &mHalPreviewWindow %p mHalPreviewWindow.user %p", __FUNCTION__,
+ &mHalPreviewWindow, mHalPreviewWindow.user);
+ return mDevice->ops->set_preview_window(mDevice,
+ buf.get() ? &mHalPreviewWindow.nw : 0);
+ }
+ return INVALID_OPERATION;
+ }
+
+ /** Set the notification and data callbacks */
+ void setCallbacks(notify_callback notify_cb,
+ data_callback data_cb,
+ data_callback_timestamp data_cb_timestamp,
+ void* user)
+ {
+ mNotifyCb = notify_cb;
+ mDataCb = data_cb;
+ mDataCbTimestamp = data_cb_timestamp;
+ mCbUser = user;
+
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+
+ if (mDevice->ops->set_callbacks) {
+ mDevice->ops->set_callbacks(mDevice,
+ __notify_cb,
+ __data_cb,
+ __data_cb_timestamp,
+ __get_memory,
+ this);
+ }
+ }
+
+ /**
+ * The following three functions all take a msgtype,
+ * which is a bitmask of the messages defined in
+ * include/ui/Camera.h
+ */
+
+ /**
+ * Enable a message, or set of messages.
+ */
+ void enableMsgType(int32_t msgType)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->enable_msg_type)
+ mDevice->ops->enable_msg_type(mDevice, msgType);
+ }
+
+ /**
+ * Disable a message, or a set of messages.
+ *
+ * Once received a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), camera hal
+ * should not rely on its client to call releaseRecordingFrame() to release
+ * video recording frames sent out by the cameral hal before and after the
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME) call. Camera hal clients must not
+ * modify/access any video recording frame after calling
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME).
+ */
+ void disableMsgType(int32_t msgType)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->disable_msg_type)
+ mDevice->ops->disable_msg_type(mDevice, msgType);
+ }
+
+ /**
+ * Query whether a message, or a set of messages, is enabled.
+ * Note that this is operates as an AND, if any of the messages
+ * queried are off, this will return false.
+ */
+ int msgTypeEnabled(int32_t msgType)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->msg_type_enabled)
+ return mDevice->ops->msg_type_enabled(mDevice, msgType);
+ return false;
+ }
+
+ /**
+ * Start preview mode.
+ */
+ status_t startPreview()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->start_preview)
+ return mDevice->ops->start_preview(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Stop a previously started preview.
+ */
+ void stopPreview()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->stop_preview)
+ mDevice->ops->stop_preview(mDevice);
+ }
+
+ /**
+ * Returns true if preview is enabled.
+ */
+ int previewEnabled()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->preview_enabled)
+ return mDevice->ops->preview_enabled(mDevice);
+ return false;
+ }
+
+ /**
+ * Request the camera hal to store meta data or real YUV data in
+ * the video buffers send out via CAMERA_MSG_VIDEO_FRRAME for a
+ * recording session. If it is not called, the default camera
+ * hal behavior is to store real YUV data in the video buffers.
+ *
+ * This method should be called before startRecording() in order
+ * to be effective.
+ *
+ * If meta data is stored in the video buffers, it is up to the
+ * receiver of the video buffers to interpret the contents and
+ * to find the actual frame data with the help of the meta data
+ * in the buffer. How this is done is outside of the scope of
+ * this method.
+ *
+ * Some camera hal may not support storing meta data in the video
+ * buffers, but all camera hal should support storing real YUV data
+ * in the video buffers. If the camera hal does not support storing
+ * the meta data in the video buffers when it is requested to do
+ * do, INVALID_OPERATION must be returned. It is very useful for
+ * the camera hal to pass meta data rather than the actual frame
+ * data directly to the video encoder, since the amount of the
+ * uncompressed frame data can be very large if video size is large.
+ *
+ * @param enable if true to instruct the camera hal to store
+ * meta data in the video buffers; false to instruct
+ * the camera hal to store real YUV data in the video
+ * buffers.
+ *
+ * @return OK on success.
+ */
+
+ status_t storeMetaDataInBuffers(int enable)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->store_meta_data_in_buffers)
+ return mDevice->ops->store_meta_data_in_buffers(mDevice, enable);
+ return enable ? INVALID_OPERATION: OK;
+ }
+
+ /**
+ * Start record mode. When a record image is available a CAMERA_MSG_VIDEO_FRAME
+ * message is sent with the corresponding frame. Every record frame must be released
+ * by a cameral hal client via releaseRecordingFrame() before the client calls
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME). After the client calls
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's responsibility
+ * to manage the life-cycle of the video recording frames, and the client must
+ * not modify/access any video recording frames.
+ */
+ status_t startRecording()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->start_recording)
+ return mDevice->ops->start_recording(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Stop a previously started recording.
+ */
+ void stopRecording()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->stop_recording)
+ mDevice->ops->stop_recording(mDevice);
+ }
+
+ /**
+ * Returns true if recording is enabled.
+ */
+ int recordingEnabled()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->recording_enabled)
+ return mDevice->ops->recording_enabled(mDevice);
+ return false;
+ }
+
+ /**
+ * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME.
+ *
+ * It is camera hal client's responsibility to release video recording
+ * frames sent out by the camera hal before the camera hal receives
+ * a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME). After it receives
+ * the call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's
+ * responsibility of managing the life-cycle of the video recording
+ * frames.
+ */
+ void releaseRecordingFrame(const sp<IMemory>& mem)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->release_recording_frame) {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ void *data = ((uint8_t *)heap->base()) + offset;
+ return mDevice->ops->release_recording_frame(mDevice, data);
+ }
+ }
+
+ /**
+ * Start auto focus, the notification callback routine is called
+ * with CAMERA_MSG_FOCUS once when focusing is complete. autoFocus()
+ * will be called again if another auto focus is needed.
+ */
+ status_t autoFocus()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->auto_focus)
+ return mDevice->ops->auto_focus(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Cancels auto-focus function. If the auto-focus is still in progress,
+ * this function will cancel it. Whether the auto-focus is in progress
+ * or not, this function will return the focus position to the default.
+ * If the camera does not support auto-focus, this is a no-op.
+ */
+ status_t cancelAutoFocus()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->cancel_auto_focus)
+ return mDevice->ops->cancel_auto_focus(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Take a picture.
+ */
+ status_t takePicture()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->take_picture)
+ return mDevice->ops->take_picture(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Cancel a picture that was started with takePicture. Calling this
+ * method when no picture is being taken is a no-op.
+ */
+ status_t cancelPicture()
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->cancel_picture)
+ return mDevice->ops->cancel_picture(mDevice);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Set the camera parameters. This returns BAD_VALUE if any parameter is
+ * invalid or not supported. */
+ status_t setParameters(const CameraParameters &params)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->set_parameters)
+ return mDevice->ops->set_parameters(mDevice,
+ params.flatten().string());
+ return INVALID_OPERATION;
+ }
+
+ /** Return the camera parameters. */
+ CameraParameters getParameters() const
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ CameraParameters parms;
+ if (mDevice->ops->get_parameters) {
+ char *temp = mDevice->ops->get_parameters(mDevice);
+ String8 str_parms(temp);
+ free(temp);
+ parms.unflatten(str_parms);
+ }
+ return parms;
+ }
+
+ /**
+ * Send command to camera driver.
+ */
+ status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->send_command)
+ return mDevice->ops->send_command(mDevice, cmd, arg1, arg2);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * Release the hardware resources owned by this object. Note that this is
+ * *not* done in the destructor.
+ */
+ void release() {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->release)
+ mDevice->ops->release(mDevice);
+ }
+
+ /**
+ * Dump state of the camera hardware
+ */
+ status_t dump(int fd, const Vector<String16>& args) const
+ {
+ LOGV("%s(%s)", __FUNCTION__, mName.string());
+ if (mDevice->ops->dump)
+ return mDevice->ops->dump(mDevice, fd);
+ return OK; // It's fine if the HAL doesn't implement dump()
+ }
+
+private:
+ camera_device_t *mDevice;
+ String8 mName;
+
+ static void __notify_cb(int32_t msg_type, int32_t ext1,
+ int32_t ext2, void *user)
+ {
+ LOGV("%s", __FUNCTION__);
+ CameraHardwareInterface *__this =
+ static_cast<CameraHardwareInterface *>(user);
+ __this->mNotifyCb(msg_type, ext1, ext2, __this->mCbUser);
+ }
+
+ static void __data_cb(int32_t msg_type,
+ const camera_memory_t *data,
+ void *user)
+ {
+ LOGV("%s", __FUNCTION__);
+ CameraHardwareInterface *__this =
+ static_cast<CameraHardwareInterface *>(user);
+ sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
+ __this->mDataCb(msg_type, mem, __this->mCbUser);
+ }
+
+ static void __data_cb_timestamp(nsecs_t timestamp, int32_t msg_type,
+ const camera_memory_t *data,
+ void *user)
+ {
+ LOGV("%s", __FUNCTION__);
+ CameraHardwareInterface *__this =
+ static_cast<CameraHardwareInterface *>(user);
+ // Start refcounting the heap object from here on. When the clients
+ // drop all references, it will be destroyed (as well as the enclosed
+ // MemoryHeapBase.
+ sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
+ __this->mDataCbTimestamp(timestamp, msg_type, mem, __this->mCbUser);
+ }
+
+ // This is a utility class that combines a MemoryHeapBase and a MemoryBase
+ // in one. Since we tend to use them in a one-to-one relationship, this is
+ // handy.
+
+ class CameraHeapMemory : public MemoryBase {
+ public:
+ CameraHeapMemory(size_t size) :
+ MemoryBase(new MemoryHeapBase(size), 0, size)
+ {
+ handle.data = getHeap()->base();
+ handle.size = size;
+ handle.handle = this;
+ }
+
+ camera_memory_t handle;
+ };
+
+ static camera_memory_t* __get_memory(size_t size,
+ void *user __attribute__((unused)))
+ {
+ // We allocate the object here, but we do not assign it to a strong
+ // pointer yet. The HAL will pass it back to us via the data callback
+ // or the data-timestamp callback, and from there on we will wrap it
+ // within a strong pointer.
+
+ CameraHeapMemory *mem = new CameraHeapMemory(size);
+ return &mem->handle;
+ }
+
+ static ANativeWindow *__to_anw(void *user)
+ {
+ CameraHardwareInterface *__this =
+ reinterpret_cast<CameraHardwareInterface *>(user);
+ return __this->mPreviewWindow.get();
+ }
+#define anw(n) __to_anw(((struct camera_preview_window *)n)->user)
+
+ static int __dequeue_buffer(struct preview_stream_ops* w,
+ buffer_handle_t** buffer)
+ {
+ int rc;
+ ANativeWindow *a = anw(w);
+ ANativeWindowBuffer* anb;
+ rc = a->dequeueBuffer(a, &anb);
+ if (!rc) {
+ rc = a->lockBuffer(a, anb);
+ if (!rc)
+ *buffer = &anb->handle;
+ else
+ a->cancelBuffer(a, anb);
+ }
+ return rc;
+ }
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *) 0)->member) *__mptr = (ptr); \
+ (type *) ((char *) __mptr - (char *)(&((type *)0)->member)); })
+#endif
+
+ static int __enqueue_buffer(struct preview_stream_ops* w,
+ buffer_handle_t* buffer)
+ {
+ ANativeWindow *a = anw(w);
+ return a->queueBuffer(a,
+ container_of(buffer, ANativeWindowBuffer, handle));
+ }
+
+ static int __cancel_buffer(struct preview_stream_ops* w,
+ buffer_handle_t* buffer)
+ {
+ ANativeWindow *a = anw(w);
+ return a->cancelBuffer(a,
+ container_of(buffer, ANativeWindowBuffer, handle));
+ }
+
+ static int __set_buffer_count(struct preview_stream_ops* w, int count)
+ {
+ ANativeWindow *a = anw(w);
+ return native_window_set_buffer_count(a, count);
+ }
+
+ static int __set_buffers_geometry(struct preview_stream_ops* w,
+ int width, int height, int format)
+ {
+ ANativeWindow *a = anw(w);
+ return native_window_set_buffers_geometry(a,
+ width, height, format);
+ }
+
+ static int __set_crop(struct preview_stream_ops *w,
+ int left, int top, int right, int bottom)
+ {
+ ANativeWindow *a = anw(w);
+ android_native_rect_t crop;
+ crop.left = left;
+ crop.top = top;
+ crop.right = right;
+ crop.bottom = bottom;
+ return native_window_set_crop(a, &crop);
+ }
+
+ static int __set_usage(struct preview_stream_ops* w, int usage)
+ {
+ ANativeWindow *a = anw(w);
+ return native_window_set_usage(a, usage);
+ }
+
+ static int __set_swap_interval(struct preview_stream_ops *w, int interval)
+ {
+ ANativeWindow *a = anw(w);
+ return a->setSwapInterval(a, interval);
+ }
+
+ static int __get_min_undequeued_buffer_count(
+ const struct preview_stream_ops *w,
+ int *count)
+ {
+ ANativeWindow *a = anw(w);
+ return a->query(a, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, count);
+ }
+
+ void initHalPreviewWindow()
+ {
+ mHalPreviewWindow.nw.cancel_buffer = __cancel_buffer;
+ mHalPreviewWindow.nw.dequeue_buffer = __dequeue_buffer;
+ mHalPreviewWindow.nw.enqueue_buffer = __enqueue_buffer;
+ mHalPreviewWindow.nw.set_buffer_count = __set_buffer_count;
+ mHalPreviewWindow.nw.set_buffers_geometry = __set_buffers_geometry;
+ mHalPreviewWindow.nw.set_crop = __set_crop;
+ mHalPreviewWindow.nw.set_usage = __set_usage;
+ mHalPreviewWindow.nw.set_swap_interval = __set_swap_interval;
+
+ mHalPreviewWindow.nw.get_min_undequeued_buffer_count =
+ __get_min_undequeued_buffer_count;
+ }
+
+ sp<ANativeWindow> mPreviewWindow;
+
+ struct camera_preview_window {
+ struct preview_stream_ops nw;
+ void *user;
+ };
+
+ struct camera_preview_window mHalPreviewWindow;
+
+ notify_callback mNotifyCb;
+ data_callback mDataCb;
+ data_callback_timestamp mDataCbTimestamp;
+ void *mCbUser;
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index a09e16b..1e8c30b 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -16,6 +16,7 @@
*/
#define LOG_TAG "CameraService"
+//#define LOG_NDEBUG 0
#include <stdio.h>
#include <sys/types.h>
@@ -37,6 +38,7 @@
#include <utils/String16.h>
#include "CameraService.h"
+#include "CameraHardwareInterface.h"
namespace android {
@@ -69,22 +71,32 @@ static int getCallingUid() {
static CameraService *gCameraService;
CameraService::CameraService()
-:mSoundRef(0)
+:mSoundRef(0), mModule(0)
{
LOGI("CameraService started (pid=%d)", getpid());
+ gCameraService = this;
+}
- 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);
+void CameraService::onFirstRef()
+{
+ BnCameraService::onFirstRef();
+
+ if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
+ (const hw_module_t **)&mModule) < 0) {
+ LOGE("Could not load camera HAL module");
+ mNumberOfCameras = 0;
+ }
+ else {
+ mNumberOfCameras = mModule->get_number_of_cameras();
+ 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() {
@@ -103,12 +115,19 @@ int32_t CameraService::getNumberOfCameras() {
status_t CameraService::getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo) {
+ if (!mModule) {
+ return NO_INIT;
+ }
+
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
return BAD_VALUE;
}
- HAL_getCameraInfo(cameraId, cameraInfo);
- return OK;
+ struct camera_info info;
+ status_t rc = mModule->get_camera_info(cameraId, &info);
+ cameraInfo->facing = info.facing;
+ cameraInfo->orientation = info.orientation;
+ return rc;
}
sp<ICamera> CameraService::connect(
@@ -116,6 +135,11 @@ sp<ICamera> CameraService::connect(
int callingPid = getCallingPid();
LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId);
+ if (!mModule) {
+ LOGE("Camera HAL module not loaded");
+ return NULL;
+ }
+
sp<Client> client;
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
@@ -146,15 +170,19 @@ sp<ICamera> CameraService::connect(
return NULL;
}
- sp<CameraHardwareInterface> hardware = HAL_openCameraHardware(cameraId);
- if (hardware == NULL) {
- LOGE("Fail to open camera hardware (id=%d)", cameraId);
+ struct camera_info info;
+ if (mModule->get_camera_info(cameraId, &info) != OK) {
+ LOGE("Invalid camera id %d", cameraId);
return NULL;
}
- CameraInfo info;
- HAL_getCameraInfo(cameraId, &info);
- client = new Client(this, cameraClient, hardware, cameraId, info.facing,
- callingPid);
+
+ char camera_device_name[10];
+ snprintf(camera_device_name, sizeof(camera_device_name), "%d", cameraId);
+
+ client = new Client(this, cameraClient,
+ new CameraHardwareInterface(&mModule->common,
+ camera_device_name),
+ cameraId, info.facing, callingPid);
mClient[cameraId] = client;
LOG1("CameraService::connect X");
return client;
@@ -244,7 +272,7 @@ void CameraService::setCameraFree(int cameraId) {
static MediaPlayer* newMediaPlayer(const char *file) {
MediaPlayer* mp = new MediaPlayer();
if (mp->setDataSource(file, NULL) == NO_ERROR) {
- mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
+ mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE);
mp->prepare();
} else {
LOGE("Failed to load CameraService sounds: %s", file);
@@ -283,7 +311,7 @@ void CameraService::playSound(sound_kind kind) {
// do not play the sound if stream volume is 0
// (typically because ringer mode is silent).
int index;
- AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
+ AudioSystem::getStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, &index);
if (index != 0) {
player->seekTo(0);
player->start();
@@ -320,7 +348,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
CAMERA_MSG_FOCUS);
// Callback is disabled by default
- mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
+ mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
mPlayShutterSound = true;
cameraService->setCameraBusy(cameraId);
@@ -410,7 +438,7 @@ status_t CameraService::Client::connect(const sp<ICameraClient>& client) {
return NO_ERROR;
}
- mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
+ mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
mClientPid = callingPid;
mCameraClient = client;
@@ -472,15 +500,15 @@ status_t CameraService::Client::setPreviewDisplay(const sp<Surface>& surface) {
result = NO_ERROR;
// return if no change in surface.
- // asBinder() is safe on NULL (returns NULL)
- if (getISurface(surface)->asBinder() == mSurface) {
+ sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0);
+ if (binder == mSurface) {
return result;
}
if (mSurface != 0) {
LOG1("clearing old preview surface %p", mSurface.get());
}
- mSurface = getISurface(surface)->asBinder();
+ mSurface = binder;
mPreviewWindow = surface;
// If preview has been already started, register preview
@@ -543,7 +571,7 @@ void CameraService::Client::setPreviewCallbackFlag(int callback_flag) {
if (checkPidAndHardware() != NO_ERROR) return;
mPreviewCallbackFlag = callback_flag;
- if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK) {
+ if (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) {
enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
} else {
disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
@@ -666,20 +694,6 @@ void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) {
mHardware->releaseRecordingFrame(mem);
}
-int32_t CameraService::Client::getNumberOfVideoBuffers() const {
- LOG1("getNumberOfVideoBuffers");
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return 0;
- return mHardware->getNumberOfVideoBuffers();
-}
-
-sp<IMemory> CameraService::Client::getVideoBuffer(int32_t index) const {
- LOG1("getVideoBuffer: %d", index);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return 0;
- return mHardware->getVideoBuffer(index);
-}
-
status_t CameraService::Client::storeMetaDataInBuffers(bool enabled)
{
LOG1("storeMetaDataInBuffers: %s", enabled? "true": "false");
@@ -938,7 +952,7 @@ void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1,
switch (msgType) {
case CAMERA_MSG_SHUTTER:
// ext1 is the dimension of the yuv picture.
- client->handleShutter((image_rect_type *)ext1);
+ client->handleShutter();
break;
default:
client->handleGenericNotify(msgType, ext1, ext2);
@@ -997,9 +1011,7 @@ void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp,
}
// 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) {
+void CameraService::Client::handleShutter(void) {
if (mPlayShutterSound) {
mCameraService->playSound(SOUND_SHUTTER);
}
@@ -1025,7 +1037,7 @@ void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) {
int flags = mPreviewCallbackFlag;
// is callback enabled?
- if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
+ if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
// If the enable bit is off, the copy-out and one-shot bits are ignored
LOG2("frame callback is disabled");
mLock.unlock();
@@ -1036,17 +1048,17 @@ void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) {
sp<ICameraClient> c = mCameraClient;
// clear callback flags if no client or one-shot mode
- if (c == 0 || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
+ if (c == 0 || (mPreviewCallbackFlag & CAMERA_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);
+ mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
+ CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
}
if (c != 0) {
// Is the received frame copied out or not?
- if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
+ if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
LOG2("frame is copied");
copyFrameAndPostCopiedFrame(c, heap, offset, size);
} else {
@@ -1257,12 +1269,4 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {
return NO_ERROR;
}
-sp<ISurface> CameraService::getISurface(const sp<Surface>& surface) {
- if (surface != 0) {
- return surface->getISurface();
- } else {
- return sp<ISurface>(0);
- }
-}
-
}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 1c43b00..5e2d571 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -19,9 +19,8 @@
#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
#include <binder/BinderService.h>
-
#include <camera/ICameraService.h>
-#include <camera/CameraHardwareInterface.h>
+#include <hardware/camera.h>
/* This needs to be increased if we can have more cameras */
#define MAX_CAMERAS 2
@@ -30,6 +29,7 @@ namespace android {
class MemoryHeapBase;
class MediaPlayer;
+class CameraHardwareInterface;
class CameraService :
public BinderService<CameraService>,
@@ -53,6 +53,7 @@ public:
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);
+ virtual void onFirstRef();
enum sound_kind {
SOUND_SHUTTER = 0,
@@ -79,12 +80,6 @@ private:
sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS];
int mSoundRef; // reference count (release all MediaPlayer when 0)
- // Used by Client objects to extract the ISurface from a Surface object.
- // This is used because making Client a friend class of Surface would
- // require including this header in Surface.h since Client is a nested
- // class.
- static sp<ISurface> getISurface(const sp<Surface>& surface);
-
class Client : public BnCamera
{
public:
@@ -99,8 +94,6 @@ private:
virtual status_t startPreview();
virtual void stopPreview();
virtual bool previewEnabled();
- virtual int32_t getNumberOfVideoBuffers() const;
- virtual sp<IMemory> getVideoBuffer(int32_t index) const;
virtual status_t storeMetaDataInBuffers(bool enabled);
virtual status_t startRecording();
virtual void stopRecording();
@@ -152,7 +145,7 @@ private:
// convert client from cookie
static sp<Client> getClientFromCookie(void* user);
// handlers for messages
- void handleShutter(image_rect_type *size);
+ void handleShutter(void);
void handlePreviewData(const sp<IMemory>& mem);
void handlePostview(const sp<IMemory>& mem);
void handleRawPicture(const sp<IMemory>& mem);
@@ -207,6 +200,8 @@ private:
// is found to be disabled. It returns true if mLock is grabbed.
bool lockIfMessageWanted(int32_t msgType);
};
+
+ camera_module_t *mModule;
};
} // namespace android
diff --git a/services/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk
index cf4e42f..cf7302a 100644
--- a/services/camera/tests/CameraServiceTest/Android.mk
+++ b/services/camera/tests/CameraServiceTest/Android.mk
@@ -19,7 +19,7 @@ LOCAL_SHARED_LIBRARIES += \
libutils \
libui \
libcamera_client \
- libsurfaceflinger_client
+ libgui
# 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.
diff --git a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
index 8a228fd..f86ca47 100644
--- a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
+++ b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
@@ -830,10 +830,10 @@ public:
ASSERT(c->previewEnabled() == true);
sleep(2);
c->stopPreview();
- if ((v & FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) {
+ if ((v & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) {
cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0);
} else {
- if ((v & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) {
+ if ((v & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) {
cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 10);
} else {
cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 1);
@@ -849,7 +849,7 @@ public:
ASSERT(c->recordingEnabled() == false);
sp<MSurface> surface = new MSurface();
ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
- c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ c->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
cc->setReleaser(c.get());
c->startRecording();
ASSERT(c->recordingEnabled() == true);
@@ -870,7 +870,7 @@ public:
CameraParameters param(c->getParameters());
param.setPreviewSize(w, h);
- c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ c->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
c->setParameters(param.flatten());
c->startPreview();
diff --git a/services/input/Android.mk b/services/input/Android.mk
index d7b61fc..836c081 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -22,15 +22,16 @@ LOCAL_SRC_FILES:= \
InputManager.cpp \
InputReader.cpp \
InputWindow.cpp \
- PointerController.cpp
+ PointerController.cpp \
+ SpriteController.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libhardware \
libhardware_legacy \
- libsurfaceflinger_client \
libskia \
+ libgui \
libui
LOCAL_C_INCLUDES := \
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 853dda4..ff4b11a 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -127,9 +127,11 @@ EventHub::EventHub(void) :
mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1),
mOpeningDevices(0), mClosingDevices(0),
mOpened(false), mNeedToSendFinishedDeviceScan(false),
- mInputBufferIndex(0), mInputBufferCount(0), mInputFdIndex(0) {
+ mInputFdIndex(1) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+
memset(mSwitches, 0, sizeof(mSwitches));
+ mNumCpus = sysconf(_SC_NPROCESSORS_ONLN);
}
EventHub::~EventHub(void) {
@@ -445,17 +447,10 @@ EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
return NULL;
}
-bool EventHub::getEvent(RawEvent* outEvent) {
- outEvent->deviceId = 0;
- outEvent->type = 0;
- outEvent->scanCode = 0;
- outEvent->keyCode = 0;
- outEvent->flags = 0;
- outEvent->value = 0;
- outEvent->when = 0;
-
- // Note that we only allow one caller to getEvent(), so don't need
+size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
+ // Note that we only allow one caller to getEvents(), so don't need
// to do locking here... only when adding/removing devices.
+ LOG_ASSERT(bufferSize >= 1);
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
@@ -463,99 +458,62 @@ bool EventHub::getEvent(RawEvent* outEvent) {
mNeedToSendFinishedDeviceScan = true;
}
+ struct input_event readBuffer[bufferSize];
+
+ RawEvent* event = buffer;
+ size_t capacity = bufferSize;
for (;;) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+
// Report any devices that had last been added/removed.
- if (mClosingDevices != NULL) {
+ while (mClosingDevices) {
Device* device = mClosingDevices;
LOGV("Reporting device closed: id=%d, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
- if (device->id == mBuiltInKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = DEVICE_REMOVED;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+ event->when = now;
+ event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+ event->type = DEVICE_REMOVED;
+ event += 1;
delete device;
mNeedToSendFinishedDeviceScan = true;
- return true;
+ if (--capacity == 0) {
+ break;
+ }
}
- if (mOpeningDevices != NULL) {
+ while (mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
LOGV("Reporting device opened: id=%d, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
- if (device->id == mBuiltInKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = DEVICE_ADDED;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+ event->when = now;
+ event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+ event->type = DEVICE_ADDED;
+ event += 1;
mNeedToSendFinishedDeviceScan = true;
- return true;
+ if (--capacity == 0) {
+ break;
+ }
}
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
- outEvent->type = FINISHED_DEVICE_SCAN;
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- return true;
+ event->when = now;
+ event->type = FINISHED_DEVICE_SCAN;
+ event += 1;
+ if (--capacity == 0) {
+ break;
+ }
}
// Grab the next input event.
+ // mInputFdIndex is initially 1 because index 0 is used for inotify.
bool deviceWasRemoved = false;
- for (;;) {
- // Consume buffered input events, if any.
- if (mInputBufferIndex < mInputBufferCount) {
- const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
- const Device* device = mDevices[mInputFdIndex];
-
- LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
- (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
- if (device->id == mBuiltInKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = iev.type;
- outEvent->scanCode = iev.code;
- outEvent->flags = 0;
- if (iev.type == EV_KEY) {
- outEvent->keyCode = AKEYCODE_UNKNOWN;
- if (device->keyMap.haveKeyLayout()) {
- status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
- &outEvent->keyCode, &outEvent->flags);
- LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
- iev.code, outEvent->keyCode, outEvent->flags, err);
- }
- } else {
- outEvent->keyCode = iev.code;
- }
- outEvent->value = iev.value;
-
- // Use an event timestamp in the same timebase as
- // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
- // as expected by the rest of the system.
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- return true;
- }
-
- // Finish reading all events from devices identified in previous poll().
- // This code assumes that mInputDeviceIndex is initially 0 and that the
- // revents member of pollfd is initialized to 0 when the device is first added.
- // Since mFds[0] is used for inotify, we process regular events starting at index 1.
- mInputFdIndex += 1;
- if (mInputFdIndex >= mFds.size()) {
- break;
- }
-
+ while (mInputFdIndex < mFds.size()) {
const struct pollfd& pfd = mFds[mInputFdIndex];
if (pfd.revents & POLLIN) {
- int32_t readSize = read(pfd.fd, mInputBufferData,
- sizeof(struct input_event) * INPUT_BUFFER_SIZE);
+ int32_t readSize = read(pfd.fd, readBuffer, sizeof(struct input_event) * capacity);
if (readSize < 0) {
if (errno == ENODEV) {
deviceWasRemoved = true;
@@ -566,11 +524,60 @@ bool EventHub::getEvent(RawEvent* outEvent) {
}
} else if ((readSize % sizeof(struct input_event)) != 0) {
LOGE("could not get event (wrong size: %d)", readSize);
+ } else if (readSize == 0) { // eof
+ deviceWasRemoved = true;
+ break;
} else {
- mInputBufferCount = size_t(readSize) / sizeof(struct input_event);
- mInputBufferIndex = 0;
+ const Device* device = mDevices[mInputFdIndex];
+ int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+
+ size_t count = size_t(readSize) / sizeof(struct input_event);
+ for (size_t i = 0; i < count; i++) {
+ const struct input_event& iev = readBuffer[i];
+ LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
+ device->path.string(),
+ (int) iev.time.tv_sec, (int) iev.time.tv_usec,
+ iev.type, iev.code, iev.value);
+
+#ifdef HAVE_POSIX_CLOCKS
+ // Use the time specified in the event instead of the current time
+ // so that downstream code can get more accurate estimates of
+ // event dispatch latency from the time the event is enqueued onto
+ // the evdev client buffer.
+ //
+ // The event's timestamp fortuitously uses the same monotonic clock
+ // time base as the rest of Android. The kernel event device driver
+ // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
+ // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
+ // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
+ // system call that also queries ktime_get_ts().
+ event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
+ + nsecs_t(iev.time.tv_usec) * 1000LL;
+ LOGV("event time %lld, now %lld", event->when, now);
+#else
+ event->when = now;
+#endif
+ event->deviceId = deviceId;
+ event->type = iev.type;
+ event->scanCode = iev.code;
+ event->value = iev.value;
+ event->keyCode = AKEYCODE_UNKNOWN;
+ event->flags = 0;
+ if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
+ status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
+ &event->keyCode, &event->flags);
+ LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+ iev.code, event->keyCode, event->flags, err);
+ }
+ event += 1;
+ }
+ capacity -= count;
+ if (capacity == 0) {
+ break;
+ }
}
}
+ mInputFdIndex += 1;
}
// Handle the case where a device has been removed but INotify has not yet noticed.
@@ -586,10 +593,16 @@ bool EventHub::getEvent(RawEvent* outEvent) {
if(mFds[0].revents & POLLIN) {
readNotify(mFds[0].fd);
mFds.editItemAt(0).revents = 0;
+ mInputFdIndex = mFds.size();
continue; // report added or removed devices immediately
}
#endif
+ // Return now if we have collected any events, otherwise poll.
+ if (event != buffer) {
+ break;
+ }
+
// Poll for events. Mind the wake lock dance!
// We hold a wake lock at all times except during poll(). This works due to some
// subtle choreography. When a device driver has pending (unread) events, it acquires
@@ -598,22 +611,46 @@ bool EventHub::getEvent(RawEvent* outEvent) {
// when this happens, the EventHub holds onto its own user wake lock while the client
// is processing events. Thus the system can only sleep if there are no events
// pending or currently being processed.
+ //
+ // The timeout is advisory only. If the device is asleep, it will not wake just to
+ // service the timeout.
release_wake_lock(WAKE_LOCK_ID);
- int pollResult = poll(mFds.editArray(), mFds.size(), -1);
+ int pollResult = poll(mFds.editArray(), mFds.size(), timeoutMillis);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
- if (pollResult <= 0) {
+ if (pollResult == 0) {
+ break; // timed out
+ }
+ if (pollResult < 0) {
+ // Sleep after errors to avoid locking up the system.
+ // Hopefully the error is transient.
if (errno != EINTR) {
LOGW("poll failed (errno=%d)\n", errno);
usleep(100000);
}
+ } else {
+ // On an SMP system, it is possible for the framework to read input events
+ // faster than the kernel input device driver can produce a complete packet.
+ // Because poll() wakes up as soon as the first input event becomes available,
+ // the framework will often end up reading one event at a time until the
+ // packet is complete. Instead of one call to read() returning 71 events,
+ // it could take 71 calls to read() each returning 1 event.
+ //
+ // Sleep for a short period of time after waking up from the poll() to give
+ // the kernel time to finish writing the entire packet of input events.
+ if (mNumCpus > 1) {
+ usleep(250);
+ }
}
// Prepare to process all of the FDs we just polled.
- mInputFdIndex = 0;
+ mInputFdIndex = 1;
}
+
+ // All done, return the number of events we read.
+ return event - buffer;
}
/*
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 7053a94..4d26a95 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -157,6 +157,8 @@ public:
// Sent when all added/removed devices from the most recent scan have been reported.
// This event is always sent at least once.
FINISHED_DEVICE_SCAN = 0x30000000,
+
+ FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
};
virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
@@ -181,13 +183,18 @@ public:
virtual void addExcludedDevice(const char* deviceName) = 0;
/*
- * Wait for the next event to become available and return it.
+ * Wait for events to become available and returns them.
* After returning, the EventHub holds onto a wake lock until the next call to getEvent.
* This ensures that the device will not go to sleep while the event is being processed.
* If the device needs to remain awake longer than that, then the caller is responsible
* for taking care of it (say, by poking the power manager user activity timer).
+ *
+ * The timeout is advisory only. If the device is asleep, it will not wake just to
+ * service the timeout.
+ *
+ * Returns the number of events obtained, or 0 if the timeout expired.
*/
- virtual bool getEvent(RawEvent* outEvent) = 0;
+ virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
/*
* Query current input state.
@@ -244,7 +251,7 @@ public:
virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) const;
- virtual bool getEvent(RawEvent* outEvent);
+ virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
virtual bool hasLed(int32_t deviceId, int32_t led) const;
virtual void setLedState(int32_t deviceId, int32_t led, bool on);
@@ -331,11 +338,11 @@ private:
// device ids that report particular switches.
int32_t mSwitches[SW_MAX + 1];
- static const int INPUT_BUFFER_SIZE = 64;
- struct input_event mInputBufferData[INPUT_BUFFER_SIZE];
- size_t mInputBufferIndex;
- size_t mInputBufferCount;
+ // The index of the next file descriptor that needs to be read.
size_t mInputFdIndex;
+
+ // Set to the number of CPUs.
+ int32_t mNumCpus;
};
}; // namespace android
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index a94e0e9..5bca7ee 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -48,6 +48,9 @@
// Log debug messages about the app switch latency optimization.
#define DEBUG_APP_SWITCH 0
+// Log debug messages about hover events.
+#define DEBUG_HOVER 0
+
#include "InputDispatcher.h"
#include <cutils/log.h>
@@ -76,6 +79,22 @@ const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
// before considering it stale and dropping it.
const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
+// Motion samples that are received within this amount of time are simply coalesced
+// when batched instead of being appended. This is done because some drivers update
+// the location of pointers one at a time instead of all at once.
+// For example, when there are 10 fingers down, the input dispatcher may receive 10
+// samples in quick succession with only one finger's location changed in each sample.
+//
+// This value effectively imposes an upper bound on the touch sampling rate.
+// Touch sensors typically have a 50Hz - 200Hz sampling rate, so we expect distinct
+// samples to become available 5-20ms apart but individual finger reports can trickle
+// in over a period of 2-4ms or so.
+//
+// Empirical testing shows that a 2ms coalescing interval (500Hz) is not enough,
+// a 3ms coalescing interval (333Hz) works well most of the time and doesn't introduce
+// significant quantization noise on current hardware.
+const nsecs_t MOTION_SAMPLE_COALESCE_INTERVAL = 3 * 1000000LL; // 3ms, 333Hz
+
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -115,7 +134,9 @@ static bool isValidMotionAction(int32_t action, size_t pointerCount) {
case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_MOVE:
case AMOTION_EVENT_ACTION_OUTSIDE:
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
case AMOTION_EVENT_ACTION_SCROLL:
return true;
case AMOTION_EVENT_ACTION_POINTER_DOWN:
@@ -189,11 +210,12 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic
mPolicy(policy),
mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(NULL),
- mDispatchEnabled(true), mDispatchFrozen(false),
+ mDispatchEnabled(true), mDispatchFrozen(false), mInputFilterEnabled(false),
mFocusedWindow(NULL),
mFocusedApplication(NULL),
mCurrentInputTargetsValid(false),
- mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
+ mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE),
+ mLastHoverWindow(NULL) {
mLooper = new Looper(false);
mInboundQueue.headSentinel.refCount = -1;
@@ -246,15 +268,7 @@ void InputDispatcher::dispatchOnce() {
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
- int32_t timeoutMillis;
- if (nextWakeupTime > currentTime) {
- uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
- timeout = (timeout + 999999LL) / 1000000LL;
- timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
- } else {
- timeoutMillis = 0;
- }
-
+ int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
@@ -379,7 +393,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
// Now we have an event to dispatch.
// All events are eventually dequeued and processed this way, even if we intend to drop them.
- assert(mPendingEvent != NULL);
+ LOG_ASSERT(mPendingEvent != NULL);
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
@@ -441,7 +455,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
}
default:
- assert(false);
+ LOG_ASSERT(false);
break;
}
@@ -568,23 +582,24 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR
reason = "inbound event was dropped because it is stale";
break;
default:
- assert(false);
+ LOG_ASSERT(false);
return;
}
switch (entry->type) {
- case EventEntry::TYPE_KEY:
- synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CANCEL_NON_POINTER_EVENTS, reason);
+ case EventEntry::TYPE_KEY: {
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
+ }
case EventEntry::TYPE_MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
- synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CANCEL_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
} else {
- synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CANCEL_NON_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
}
break;
}
@@ -734,7 +749,7 @@ bool InputDispatcher::dispatchKeyLocked(
if (entry->repeatCount == 0
&& entry->action == AKEY_EVENT_ACTION_DOWN
&& (entry->policyFlags & POLICY_FLAG_TRUSTED)
- && !entry->isInjected()) {
+ && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
if (mKeyRepeatState.lastKeyEntry
&& mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
// We have seen two identical key downs in a row which indicates that the device
@@ -853,10 +868,11 @@ bool InputDispatcher::dispatchMotionLocked(
bool conflictingPointerActions = false;
if (! mCurrentInputTargetsValid) {
int32_t injectionResult;
+ const MotionSample* splitBatchAfterSample = NULL;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
injectionResult = findTouchedWindowTargetsLocked(currentTime,
- entry, nextWakeupTime, &conflictingPointerActions);
+ entry, nextWakeupTime, &conflictingPointerActions, &splitBatchAfterSample);
} else {
// Non touch event. (eg. trackball)
injectionResult = findFocusedWindowTargetsLocked(currentTime,
@@ -873,12 +889,48 @@ bool InputDispatcher::dispatchMotionLocked(
addMonitoringTargetsLocked();
commitTargetsLocked();
+
+ // Unbatch the event if necessary by splitting it into two parts after the
+ // motion sample indicated by splitBatchAfterSample.
+ if (splitBatchAfterSample && splitBatchAfterSample->next) {
+#if DEBUG_BATCHING
+ uint32_t originalSampleCount = entry->countSamples();
+#endif
+ MotionSample* nextSample = splitBatchAfterSample->next;
+ MotionEntry* nextEntry = mAllocator.obtainMotionEntry(nextSample->eventTime,
+ entry->deviceId, entry->source, entry->policyFlags,
+ entry->action, entry->flags, entry->metaState, entry->edgeFlags,
+ entry->xPrecision, entry->yPrecision, entry->downTime,
+ entry->pointerCount, entry->pointerIds, nextSample->pointerCoords);
+ if (nextSample != entry->lastSample) {
+ nextEntry->firstSample.next = nextSample->next;
+ nextEntry->lastSample = entry->lastSample;
+ }
+ mAllocator.freeMotionSample(nextSample);
+
+ entry->lastSample = const_cast<MotionSample*>(splitBatchAfterSample);
+ entry->lastSample->next = NULL;
+
+ if (entry->injectionState) {
+ nextEntry->injectionState = entry->injectionState;
+ entry->injectionState->refCount += 1;
+ }
+
+#if DEBUG_BATCHING
+ LOGD("Split batch of %d samples into two parts, first part has %d samples, "
+ "second part has %d samples.", originalSampleCount,
+ entry->countSamples(), nextEntry->countSamples());
+#endif
+
+ mInboundQueue.enqueueAtHead(nextEntry);
+ }
}
// Dispatch the motion.
if (conflictingPointerActions) {
- synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CANCEL_POINTER_EVENTS, "Conflicting pointer actions.");
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "conflicting pointer actions");
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
}
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
return true;
@@ -934,7 +986,7 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi
toString(resumeWithAppendedMotionSample));
#endif
- assert(eventEntry->dispatchInProgress); // should already have been set to true
+ LOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
pokeUserActivityLocked(eventEntry);
@@ -1044,9 +1096,9 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
if (connection->status == Connection::STATUS_NORMAL) {
- synthesizeCancelationEventsForConnectionLocked(
- connection, InputState::CANCEL_ALL_EVENTS,
+ CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
"application not responding");
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
}
@@ -1123,7 +1175,8 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));
+ addWindowTargetLocked(mFocusedWindow,
+ InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0));
// Done.
Failed:
@@ -1140,7 +1193,8 @@ Unresponsive:
}
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
- const MotionEntry* entry, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
+ const MotionEntry* entry, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions,
+ const MotionSample** outSplitBatchAfterSample) {
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
INJECTION_PERMISSION_GRANTED,
@@ -1183,14 +1237,19 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
// Update the touch state as needed based on the properties of the touch event.
int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ const InputWindow* newHoverWindow = NULL;
bool isSplit = mTouchState.split;
bool wrongDevice = mTouchState.down
&& (mTouchState.deviceId != entry->deviceId
|| mTouchState.source != entry->source);
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
- || maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
+ || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
+ || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
+ bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN
+ || maskedAction == AMOTION_EVENT_ACTION_SCROLL
+ || isHoverAction);
+ if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
if (wrongDevice && !down) {
mTempTouchState.copyFrom(mTouchState);
@@ -1206,26 +1265,25 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
mTempTouchState.copyFrom(mTouchState);
}
if (wrongDevice) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_FOCUS
LOGD("Dropping event because a pointer for a different device is already down.");
#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN
- || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
- || maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
+ const MotionSample* sample = &entry->firstSample;
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].
+ int32_t x = int32_t(sample->pointerCoords[pointerIndex].
getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].
+ int32_t y = int32_t(sample->pointerCoords[pointerIndex].
getAxisValue(AMOTION_EVENT_AXIS_Y));
const InputWindow* newTouchedWindow = NULL;
const InputWindow* topErrorWindow = NULL;
+ bool isTouchModal = false;
// Traverse windows from front to back to find touched window and outside targets.
size_t numWindows = mWindows.size();
@@ -1241,7 +1299,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
if (window->visible) {
if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
- bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
+ isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
| InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
if (isTouchModal || window->touchableRegionContainsPoint(x, y)) {
if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
@@ -1253,7 +1311,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
if (maskedAction == AMOTION_EVENT_ACTION_DOWN
&& (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) {
- int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
+ int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
if (isWindowObscuredAtPointLocked(window, x, y)) {
outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
@@ -1306,7 +1364,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
}
// Set target flags.
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
+ int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
if (isSplit) {
targetFlags |= InputTarget::FLAG_SPLIT;
}
@@ -1314,6 +1372,28 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
+ // Update hover state.
+ if (isHoverAction) {
+ newHoverWindow = newTouchedWindow;
+
+ // Ensure all subsequent motion samples are also within the touched window.
+ // Set *outSplitBatchAfterSample to the sample before the first one that is not
+ // within the touched window.
+ if (!isTouchModal) {
+ while (sample->next) {
+ if (!newHoverWindow->touchableRegionContainsPoint(
+ sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
+ sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y))) {
+ *outSplitBatchAfterSample = sample;
+ break;
+ }
+ sample = sample->next;
+ }
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ newHoverWindow = mLastHoverWindow;
+ }
+
// Update the temporary touch state.
BitSet32 pointerIds;
if (isSplit) {
@@ -1326,7 +1406,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
// If the pointer is not currently down, then ignore the event.
if (! mTempTouchState.down) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_FOCUS
LOGD("Dropping event because the pointer is not down or we previously "
"dropped the pointer down event.");
#endif
@@ -1335,6 +1415,29 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
}
}
+ if (newHoverWindow != mLastHoverWindow) {
+ // Split the batch here so we send exactly one sample as part of ENTER or EXIT.
+ *outSplitBatchAfterSample = &entry->firstSample;
+
+ // Let the previous window know that the hover sequence is over.
+ if (mLastHoverWindow) {
+#if DEBUG_HOVER
+ LOGD("Sending hover exit event to window %s.", mLastHoverWindow->name.string());
+#endif
+ mTempTouchState.addOrUpdateWindow(mLastHoverWindow,
+ InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
+ }
+
+ // Let the new window know that the hover sequence is starting.
+ if (newHoverWindow) {
+#if DEBUG_HOVER
+ LOGD("Sending hover enter event to window %s.", newHoverWindow->name.string());
+#endif
+ mTempTouchState.addOrUpdateWindow(newHoverWindow,
+ InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0));
+ }
+ }
+
// Check permission to inject into all touched foreground windows and ensure there
// is at least one touched foreground window.
{
@@ -1351,7 +1454,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
}
}
if (! haveForegroundWindow) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_FOCUS
LOGD("Dropping event because there is no touched foreground window to receive it.");
#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
@@ -1368,7 +1471,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
// If the touched window is paused then keep waiting.
if (touchedWindow.window->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_FOCUS
LOGD("Waiting because touched window is paused.");
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
@@ -1401,7 +1504,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const InputWindow* window = & mWindows[i];
if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
mTempTouchState.addOrUpdateWindow(window,
- InputTarget::FLAG_WINDOW_IS_OBSCURED, BitSet32(0));
+ InputTarget::FLAG_WINDOW_IS_OBSCURED
+ | InputTarget::FLAG_DISPATCH_AS_IS,
+ BitSet32(0));
}
}
}
@@ -1416,8 +1521,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
touchedWindow.pointerIds);
}
- // Drop the outside touch window since we will not care about them in the next iteration.
- mTempTouchState.removeOutsideTouchWindows();
+ // Drop the outside or hover touch windows since we will not care about them
+ // in the next iteration.
+ mTempTouchState.filterNonAsIsTouchWindows();
Failed:
// Check injection permission once and for all.
@@ -1434,7 +1540,7 @@ Failed:
if (!wrongDevice) {
if (maskedAction == AMOTION_EVENT_ACTION_UP
|| maskedAction == AMOTION_EVENT_ACTION_CANCEL
- || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ || isHoverAction) {
// All pointers up or canceled.
mTouchState.reset();
} else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
@@ -1471,6 +1577,9 @@ Failed:
// Save changes to touch state as-is for all other actions.
mTouchState.copyFrom(mTempTouchState);
}
+
+ // Update hover state.
+ mLastHoverWindow = newHoverWindow;
}
} else {
#if DEBUG_FOCUS
@@ -1512,9 +1621,10 @@ void InputDispatcher::addMonitoringTargetsLocked() {
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = mMonitoringChannels[i];
- target.flags = 0;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
target.xOffset = 0;
target.yOffset = 0;
+ target.pointerIds.clear();
target.scaleFactor = 1.0f;
}
}
@@ -1628,7 +1738,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
// Make sure we are never called for streaming when splitting across multiple windows.
bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
- assert(! (resumeWithAppendedMotionSample && isSplit));
+ LOG_ASSERT(! (resumeWithAppendedMotionSample && isSplit));
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
@@ -1642,7 +1752,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
// Split a motion event if needed.
if (isSplit) {
- assert(eventEntry->type == EventEntry::TYPE_MOTION);
+ LOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
@@ -1749,10 +1859,36 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
}
}
+ // Enqueue dispatch entries for the requested modes.
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_IS);
+
+ // If the outbound queue was previously empty, start the dispatch cycle going.
+ if (wasEmpty && !connection->outboundQueue.isEmpty()) {
+ activateConnectionLocked(connection.get());
+ startDispatchCycleLocked(currentTime, connection);
+ }
+}
+
+void InputDispatcher::enqueueDispatchEntryLocked(
+ const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
+ bool resumeWithAppendedMotionSample, int32_t dispatchMode) {
+ int32_t inputTargetFlags = inputTarget->flags;
+ if (!(inputTargetFlags & dispatchMode)) {
+ return;
+ }
+ inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
+
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
- inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
+ inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
if (dispatchEntry->hasForegroundTarget()) {
incrementPendingForegroundDispatchesLocked(eventEntry);
@@ -1774,12 +1910,6 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
-
- // If the outbound queue was previously empty, start the dispatch cycle going.
- if (wasEmpty) {
- activateConnectionLocked(connection.get());
- startDispatchCycleLocked(currentTime, connection);
- }
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -1789,21 +1919,18 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
connection->getInputChannelName());
#endif
- assert(connection->status == Connection::STATUS_NORMAL);
- assert(! connection->outboundQueue.isEmpty());
+ LOG_ASSERT(connection->status == Connection::STATUS_NORMAL);
+ LOG_ASSERT(! connection->outboundQueue.isEmpty());
DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
- assert(! dispatchEntry->inProgress);
+ LOG_ASSERT(! dispatchEntry->inProgress);
// Mark the dispatch entry as in progress.
dispatchEntry->inProgress = true;
- // Update the connection's input state.
- EventEntry* eventEntry = dispatchEntry->eventEntry;
- connection->inputState.trackEvent(eventEntry);
-
// Publish the event.
status_t status;
+ EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
@@ -1812,6 +1939,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
int32_t action = keyEntry->action;
int32_t flags = keyEntry->flags;
+ // Update the connection's input state.
+ connection->inputState.trackKey(keyEntry, action);
+
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
action, flags, keyEntry->keyCode, keyEntry->scanCode,
@@ -1833,8 +1963,12 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
// Apply target flags.
int32_t action = motionEntry->action;
int32_t flags = motionEntry->flags;
- if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
action = AMOTION_EVENT_ACTION_OUTSIDE;
+ } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
+ action = AMOTION_EVENT_ACTION_HOVER_EXIT;
+ } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
+ action = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
@@ -1871,6 +2005,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
scaleFactor = 1.0f;
}
+ // Update the connection's input state.
+ connection->inputState.trackMotion(motionEntry, action);
+
// Publish the motion event and the first motion sample.
status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
@@ -1885,44 +2022,47 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
return;
}
- // Append additional motion samples.
- MotionSample* nextMotionSample = firstMotionSample->next;
- for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
- if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) != 0 && scaleFactor != 1.0f) {
- for (size_t i = 0; i < motionEntry->pointerCount; i++) {
- scaledCoords[i] = nextMotionSample->pointerCoords[i];
- scaledCoords[i].scale(scaleFactor);
+ if (action == AMOTION_EVENT_ACTION_MOVE
+ || action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ // Append additional motion samples.
+ MotionSample* nextMotionSample = firstMotionSample->next;
+ for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
+ if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) != 0 && scaleFactor != 1.0f) {
+ for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+ scaledCoords[i] = nextMotionSample->pointerCoords[i];
+ scaledCoords[i].scale(scaleFactor);
+ }
+ } else {
+ usingCoords = nextMotionSample->pointerCoords;
}
- } else {
- usingCoords = nextMotionSample->pointerCoords;
- }
- status = connection->inputPublisher.appendMotionSample(
- nextMotionSample->eventTime, usingCoords);
- if (status == NO_MEMORY) {
+ status = connection->inputPublisher.appendMotionSample(
+ nextMotionSample->eventTime, usingCoords);
+ if (status == NO_MEMORY) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will "
"be sent in the next dispatch cycle.",
connection->getInputChannelName());
#endif
- break;
- }
- if (status != OK) {
- LOGE("channel '%s' ~ Could not append motion sample "
- "for a reason other than out of memory, status=%d",
- connection->getInputChannelName(), status);
- abortBrokenDispatchCycleLocked(currentTime, connection);
- return;
+ break;
+ }
+ if (status != OK) {
+ LOGE("channel '%s' ~ Could not append motion sample "
+ "for a reason other than out of memory, status=%d",
+ connection->getInputChannelName(), status);
+ abortBrokenDispatchCycleLocked(currentTime, connection);
+ return;
+ }
}
- }
- // Remember the next motion sample that we could not dispatch, in case we ran out
- // of space in the shared memory buffer.
- dispatchEntry->tailMotionSample = nextMotionSample;
+ // Remember the next motion sample that we could not dispatch, in case we ran out
+ // of space in the shared memory buffer.
+ dispatchEntry->tailMotionSample = nextMotionSample;
+ }
break;
}
default: {
- assert(false);
+ LOG_ASSERT(false);
}
}
@@ -2088,26 +2228,24 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data
}
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CancelationOptions options, const char* reason) {
+ const CancelationOptions& options) {
for (size_t i = 0; i < mConnectionsByReceiveFd.size(); i++) {
synthesizeCancelationEventsForConnectionLocked(
- mConnectionsByReceiveFd.valueAt(i), options, reason);
+ mConnectionsByReceiveFd.valueAt(i), options);
}
}
void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
- const sp<InputChannel>& channel, InputState::CancelationOptions options,
- const char* reason) {
+ const sp<InputChannel>& channel, const CancelationOptions& options) {
ssize_t index = getConnectionIndexLocked(channel);
if (index >= 0) {
synthesizeCancelationEventsForConnectionLocked(
- mConnectionsByReceiveFd.valueAt(index), options, reason);
+ mConnectionsByReceiveFd.valueAt(index), options);
}
}
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
- const sp<Connection>& connection, InputState::CancelationOptions options,
- const char* reason) {
+ const sp<Connection>& connection, const CancelationOptions& options) {
nsecs_t currentTime = now();
mTempCancelationEvents.clear();
@@ -2118,8 +2256,9 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
&& connection->status != Connection::STATUS_BROKEN) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync "
- "with reality: %s, options=%d.",
- connection->getInputChannelName(), mTempCancelationEvents.size(), reason, options);
+ "with reality: %s, mode=%d.",
+ connection->getInputChannelName(), mTempCancelationEvents.size(),
+ options.reason, options.mode);
#endif
for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
@@ -2163,7 +2302,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
InputDispatcher::MotionEntry*
InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
- assert(pointerIds.value != 0);
+ LOG_ASSERT(pointerIds.value != 0);
uint32_t splitPointerIndexMap[MAX_POINTERS];
int32_t splitPointerIds[MAX_POINTERS];
@@ -2178,8 +2317,8 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet
if (pointerIds.hasBit(pointerId)) {
splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
splitPointerIds[splitPointerCount] = pointerId;
- splitPointerCoords[splitPointerCount] =
- originalMotionEntry->firstSample.pointerCoords[originalPointerIndex];
+ splitPointerCoords[splitPointerCount].copyFrom(
+ originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]);
splitPointerCount += 1;
}
}
@@ -2242,14 +2381,19 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet
for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount;
splitPointerIndex++) {
uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex];
- splitPointerCoords[splitPointerIndex] =
- originalMotionSample->pointerCoords[originalPointerIndex];
+ splitPointerCoords[splitPointerIndex].copyFrom(
+ originalMotionSample->pointerCoords[originalPointerIndex]);
}
mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime,
splitPointerCoords);
}
+ if (originalMotionEntry->injectionState) {
+ splitMotionEntry->injectionState = originalMotionEntry->injectionState;
+ splitMotionEntry->injectionState->refCount += 1;
+ }
+
return splitMotionEntry;
}
@@ -2318,7 +2462,18 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t so
bool needWake;
{ // acquire lock
- AutoMutex _l(mLock);
+ mLock.lock();
+
+ if (mInputFilterEnabled) {
+ mLock.unlock();
+
+ policyFlags |= POLICY_FLAG_FILTERED;
+ if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+ return; // event was consumed by the filter
+ }
+
+ mLock.lock();
+ }
int32_t repeatCount = 0;
KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
@@ -2326,6 +2481,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t so
metaState, repeatCount, downTime);
needWake = enqueueInboundEventLocked(newEntry);
+ mLock.unlock();
} // release lock
if (needWake) {
@@ -2368,7 +2524,23 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
bool needWake;
{ // acquire lock
- AutoMutex _l(mLock);
+ mLock.lock();
+
+ if (mInputFilterEnabled) {
+ mLock.unlock();
+
+ MotionEvent event;
+ event.initialize(deviceId, source, action, flags, edgeFlags, metaState, 0, 0,
+ xPrecision, yPrecision, downTime, eventTime,
+ pointerCount, pointerIds, pointerCoords);
+
+ policyFlags |= POLICY_FLAG_FILTERED;
+ if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+ return; // event was consumed by the filter
+ }
+
+ mLock.lock();
+ }
// Attempt batching and streaming of move events.
if (action == AMOTION_EVENT_ACTION_MOVE
@@ -2392,24 +2564,43 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
continue;
}
- if (motionEntry->action != action
- || motionEntry->pointerCount != pointerCount
- || motionEntry->isInjected()) {
+ if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) {
// Last motion event in the queue for this device and source is
// not compatible for appending new samples. Stop here.
goto NoBatchingOrStreaming;
}
- // The last motion event is a move and is compatible for appending.
// Do the batching magic.
- mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
-#if DEBUG_BATCHING
- LOGD("Appended motion sample onto batch for most recent "
- "motion event for this device in the inbound queue.");
-#endif
+ batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords,
+ "most recent motion event for this device and source in the inbound queue");
+ mLock.unlock();
return; // done!
}
+ // BATCHING ONTO PENDING EVENT CASE
+ //
+ // Try to append a move sample to the currently pending event, if there is one.
+ // We can do this as long as we are still waiting to find the targets for the
+ // event. Once the targets are locked-in we can only do streaming.
+ if (mPendingEvent
+ && (!mPendingEvent->dispatchInProgress || !mCurrentInputTargetsValid)
+ && mPendingEvent->type == EventEntry::TYPE_MOTION) {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(mPendingEvent);
+ if (motionEntry->deviceId == deviceId && motionEntry->source == source) {
+ if (!motionEntry->canAppendSamples(action, pointerCount, pointerIds)) {
+ // Pending motion event is for this device and source but it is
+ // not compatible for appending new samples. Stop here.
+ goto NoBatchingOrStreaming;
+ }
+
+ // Do the batching magic.
+ batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords,
+ "pending motion event");
+ mLock.unlock();
+ return; // done!
+ }
+ }
+
// STREAMING CASE
//
// There is no pending motion event (of any kind) for this device in the inbound queue.
@@ -2459,12 +2650,35 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
continue;
}
+ if (action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ if (!mLastHoverWindow) {
+#if DEBUG_BATCHING
+ LOGD("Not streaming hover move because there is no "
+ "last hovered window.");
+#endif
+ goto NoBatchingOrStreaming;
+ }
+
+ const InputWindow* hoverWindow = findTouchedWindowAtLocked(
+ pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
+ pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ if (mLastHoverWindow != hoverWindow) {
+#if DEBUG_BATCHING
+ LOGD("Not streaming hover move because the last hovered window "
+ "is '%s' but the currently hovered window is '%s'.",
+ mLastHoverWindow->name.string(),
+ hoverWindow ? hoverWindow->name.string() : "<null>");
+#endif
+ goto NoBatchingOrStreaming;
+ }
+ }
+
// Hurray! This foreground target is currently dispatching a move event
// that we can stream onto. Append the motion sample and resume dispatch.
mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
#if DEBUG_BATCHING
LOGD("Appended motion sample onto batch for most recently dispatched "
- "motion event for this device in the outbound queues. "
+ "motion event for this device and source in the outbound queues. "
"Attempting to stream the motion sample.");
#endif
nsecs_t currentTime = now();
@@ -2472,6 +2686,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t
true /*resumeWithAppendedMotionSample*/);
runCommandsLockedInterruptible();
+ mLock.unlock();
return; // done!
}
}
@@ -2486,6 +2701,7 @@ NoBatchingOrStreaming:;
pointerCount, pointerIds, pointerCoords);
needWake = enqueueInboundEventLocked(newEntry);
+ mLock.unlock();
} // release lock
if (needWake) {
@@ -2493,6 +2709,36 @@ NoBatchingOrStreaming:;
}
}
+void InputDispatcher::batchMotionLocked(MotionEntry* entry, nsecs_t eventTime,
+ int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription) {
+ // Combine meta states.
+ entry->metaState |= metaState;
+
+ // Coalesce this sample if not enough time has elapsed since the last sample was
+ // initially appended to the batch.
+ MotionSample* lastSample = entry->lastSample;
+ long interval = eventTime - lastSample->eventTimeBeforeCoalescing;
+ if (interval <= MOTION_SAMPLE_COALESCE_INTERVAL) {
+ uint32_t pointerCount = entry->pointerCount;
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ lastSample->pointerCoords[i].copyFrom(pointerCoords[i]);
+ }
+ lastSample->eventTime = eventTime;
+#if DEBUG_BATCHING
+ LOGD("Coalesced motion into last sample of batch for %s, events were %0.3f ms apart",
+ eventDescription, interval * 0.000001f);
+#endif
+ return;
+ }
+
+ // Append the sample.
+ mAllocator.appendMotionSample(entry, eventTime, pointerCoords);
+#if DEBUG_BATCHING
+ LOGD("Appended motion sample onto batch for %s, events were %0.3f ms apart",
+ eventDescription, interval * 0.000001f);
+#endif
+}
+
void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
@@ -2505,16 +2751,17 @@ void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t swi
}
int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "syncMode=%d, timeoutMillis=%d",
- event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
+ "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
+ event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
#endif
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
- uint32_t policyFlags = POLICY_FLAG_INJECTED;
+ policyFlags |= POLICY_FLAG_INJECTED;
if (hasInjectionPermission(injectorPid, injectorUid)) {
policyFlags |= POLICY_FLAG_TRUSTED;
}
@@ -2533,7 +2780,9 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
policyFlags |= POLICY_FLAG_VIRTUAL;
}
- mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+ if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+ mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+ }
if (policyFlags & POLICY_FLAG_WOKE_HERE) {
flags |= AKEY_EVENT_FLAG_WOKE_HERE;
@@ -2557,8 +2806,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
return INPUT_EVENT_INJECTION_FAILED;
}
- nsecs_t eventTime = motionEvent->getEventTime();
- mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
+ if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+ nsecs_t eventTime = motionEvent->getEventTime();
+ mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
+ }
mLock.lock();
const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
@@ -2673,7 +2924,8 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject
injectionResult, injectionState->injectorPid, injectionState->injectorUid);
#endif
- if (injectionState->injectionIsAsync) {
+ if (injectionState->injectionIsAsync
+ && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
switch (injectionResult) {
case INPUT_EVENT_INJECTION_SUCCEEDED:
@@ -2737,6 +2989,11 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
oldFocusedWindowChannel = mFocusedWindow->inputChannel;
mFocusedWindow = NULL;
}
+ sp<InputChannel> oldLastHoverWindowChannel;
+ if (mLastHoverWindow) {
+ oldLastHoverWindowChannel = mLastHoverWindow->inputChannel;
+ mLastHoverWindow = NULL;
+ }
mWindows.clear();
@@ -2759,8 +3016,9 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
LOGD("Focus left window: %s",
oldFocusedWindowChannel->getName().string());
#endif
- synthesizeCancelationEventsForInputChannelLocked(oldFocusedWindowChannel,
- InputState::CANCEL_NON_POINTER_EVENTS, "focus left window");
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ "focus left window");
+ synthesizeCancelationEventsForInputChannelLocked(oldFocusedWindowChannel, options);
oldFocusedWindowChannel.clear();
}
}
@@ -2781,12 +3039,19 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
#if DEBUG_FOCUS
LOGD("Touched window was removed: %s", touchedWindow.channel->getName().string());
#endif
- synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel,
- InputState::CANCEL_POINTER_EVENTS, "touched window was removed");
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "touched window was removed");
+ synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel, options);
mTouchState.windows.removeAt(i);
}
}
+ // Recover the last hovered window.
+ if (oldLastHoverWindowChannel != NULL) {
+ mLastHoverWindow = getWindowLocked(oldLastHoverWindowChannel);
+ oldLastHoverWindowChannel.clear();
+ }
+
#if DEBUG_FOCUS
//logDispatchStateLocked();
#endif
@@ -2862,6 +3127,26 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
}
}
+void InputDispatcher::setInputFilterEnabled(bool enabled) {
+#if DEBUG_FOCUS
+ LOGD("setInputFilterEnabled: enabled=%d", enabled);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mInputFilterEnabled == enabled) {
+ return;
+ }
+
+ mInputFilterEnabled = enabled;
+ resetAndDropEverythingLocked("input filter is being enabled or disabled");
+ } // release lock
+
+ // Wake up poll loop since there might be work to do to drop everything.
+ mLooper->wake();
+}
+
bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
const sp<InputChannel>& toChannel) {
#if DEBUG_FOCUS
@@ -2896,7 +3181,8 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
mTouchState.windows.removeAt(i);
int32_t newTargetFlags = oldTargetFlags
- & (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT);
+ & (InputTarget::FLAG_FOREGROUND
+ | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
mTouchState.addOrUpdateWindow(toWindow, newTargetFlags, pointerIds);
found = true;
@@ -2918,9 +3204,9 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
sp<Connection> toConnection = mConnectionsByReceiveFd.valueAt(toConnectionIndex);
fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
- synthesizeCancelationEventsForConnectionLocked(fromConnection,
- InputState::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"transferring touch focus from this window to another window");
+ synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
}
#if DEBUG_FOCUS
@@ -2938,7 +3224,8 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
LOGD("Resetting and dropping all events (%s).", reason);
#endif
- synthesizeCancelationEventsForAllConnectionsLocked(InputState::CANCEL_ALL_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
resetKeyRepeatLocked();
releasePendingEventLocked();
@@ -3269,58 +3556,49 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
if (!connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
if (dispatchEntry->inProgress
- && dispatchEntry->hasForegroundTarget()
&& dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
if (!(keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
- if (handled) {
- // If the application handled a non-fallback key, then immediately
- // cancel all fallback keys previously dispatched to the application.
- // This behavior will prevent chording with fallback keys (so they cannot
- // be used as modifiers) but it will ensure that fallback keys do not
- // get stuck. This takes care of the case where the application does not handle
- // the original DOWN so we generate a fallback DOWN but it does handle
- // the original UP in which case we want to send a fallback CANCEL.
- synthesizeCancelationEventsForConnectionLocked(connection,
- InputState::CANCEL_FALLBACK_EVENTS,
- "application handled a non-fallback event, "
- "canceling all fallback events");
- connection->originalKeyCodeForFallback = -1;
+ // Get the fallback key state.
+ // Clear it out after dispatching the UP.
+ int32_t originalKeyCode = keyEntry->keyCode;
+ int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
+ if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+ connection->inputState.removeFallbackKey(originalKeyCode);
+ }
+
+ if (handled || !dispatchEntry->hasForegroundTarget()) {
+ // If the application handles the original key for which we previously
+ // generated a fallback or if the window is not a foreground window,
+ // then cancel the associated fallback key, if any.
+ if (fallbackKeyCode != -1) {
+ if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
+ CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ "application handled the original non-fallback key "
+ "or is no longer a foreground target, "
+ "canceling previously dispatched fallback key");
+ options.keyCode = fallbackKeyCode;
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
+ }
+ connection->inputState.removeFallbackKey(originalKeyCode);
+ }
} else {
// If the application did not handle a non-fallback key, first check
- // that we are in a good state to handle the fallback key. Then ask
- // the policy what to do with it.
- if (connection->originalKeyCodeForFallback < 0) {
- if (keyEntry->action != AKEY_EVENT_ACTION_DOWN
- || keyEntry->repeatCount != 0) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("Unhandled key event: Skipping fallback since this "
- "is not an initial down. "
- "keyCode=%d, action=%d, repeatCount=%d",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount);
-#endif
- goto SkipFallback;
- }
-
- // Start handling the fallback key on DOWN.
- connection->originalKeyCodeForFallback = keyEntry->keyCode;
- } else {
- if (keyEntry->keyCode != connection->originalKeyCodeForFallback) {
+ // that we are in a good state to perform unhandled key event processing
+ // Then ask the policy what to do with it.
+ bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
+ && keyEntry->repeatCount == 0;
+ if (fallbackKeyCode == -1 && !initialDown) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("Unhandled key event: Skipping fallback since there is "
- "already a different fallback in progress. "
- "keyCode=%d, originalKeyCodeForFallback=%d",
- keyEntry->keyCode, connection->originalKeyCodeForFallback);
+ LOGD("Unhandled key event: Skipping unhandled key event processing "
+ "since this is not an initial down. "
+ "keyCode=%d, action=%d, repeatCount=%d",
+ originalKeyCode, keyEntry->action, keyEntry->repeatCount);
#endif
- goto SkipFallback;
- }
-
- // Finish handling the fallback key on UP.
- if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- connection->originalKeyCodeForFallback = -1;
- }
+ goto SkipFallback;
}
+ // Dispatch the unhandled key to the policy.
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("Unhandled key event: Asking policy to perform fallback action. "
"keyCode=%d, action=%d, repeatCount=%d",
@@ -3337,10 +3615,70 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
mLock.lock();
if (connection->status != Connection::STATUS_NORMAL) {
+ connection->inputState.removeFallbackKey(originalKeyCode);
return;
}
- assert(connection->outboundQueue.headSentinel.next == dispatchEntry);
+ LOG_ASSERT(connection->outboundQueue.headSentinel.next == dispatchEntry);
+
+ // Latch the fallback keycode for this key on an initial down.
+ // The fallback keycode cannot change at any other point in the lifecycle.
+ if (initialDown) {
+ if (fallback) {
+ fallbackKeyCode = event.getKeyCode();
+ } else {
+ fallbackKeyCode = AKEYCODE_UNKNOWN;
+ }
+ connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
+ }
+
+ LOG_ASSERT(fallbackKeyCode != -1);
+
+ // Cancel the fallback key if the policy decides not to send it anymore.
+ // We will continue to dispatch the key to the policy but we will no
+ // longer dispatch a fallback key to the application.
+ if (fallbackKeyCode != AKEYCODE_UNKNOWN
+ && (!fallback || fallbackKeyCode != event.getKeyCode())) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ if (fallback) {
+ LOGD("Unhandled key event: Policy requested to send key %d"
+ "as a fallback for %d, but on the DOWN it had requested "
+ "to send %d instead. Fallback canceled.",
+ event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+ } else {
+ LOGD("Unhandled key event: Policy did not request fallback for %d,"
+ "but on the DOWN it had requested to send %d. "
+ "Fallback canceled.",
+ originalKeyCode, fallbackKeyCode);
+ }
+#endif
+
+ CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ "canceling fallback, policy no longer desires it");
+ options.keyCode = fallbackKeyCode;
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
+
+ fallback = false;
+ fallbackKeyCode = AKEYCODE_UNKNOWN;
+ if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
+ connection->inputState.setFallbackKey(originalKeyCode,
+ fallbackKeyCode);
+ }
+ }
+
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ {
+ String8 msg;
+ const KeyedVector<int32_t, int32_t>& fallbackKeys =
+ connection->inputState.getFallbackKeys();
+ for (size_t i = 0; i < fallbackKeys.size(); i++) {
+ msg.appendFormat(", %d->%d", fallbackKeys.keyAt(i),
+ fallbackKeys.valueAt(i));
+ }
+ LOGD("Unhandled key event: %d currently tracked fallback keys%s.",
+ fallbackKeys.size(), msg.string());
+ }
+#endif
if (fallback) {
// Restart the dispatch cycle using the fallback key.
@@ -3348,7 +3686,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
keyEntry->deviceId = event.getDeviceId();
keyEntry->source = event.getSource();
keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
- keyEntry->keyCode = event.getKeyCode();
+ keyEntry->keyCode = fallbackKeyCode;
keyEntry->scanCode = event.getScanCode();
keyEntry->metaState = event.getMetaState();
keyEntry->repeatCount = event.getRepeatCount();
@@ -3357,13 +3695,17 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("Unhandled key event: Dispatching fallback key. "
- "fallbackKeyCode=%d, fallbackMetaState=%08x",
- keyEntry->keyCode, keyEntry->metaState);
+ "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+ originalKeyCode, fallbackKeyCode, keyEntry->metaState);
#endif
dispatchEntry->inProgress = false;
startDispatchCycleLocked(now(), connection);
return;
+ } else {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("Unhandled key event: No fallback key.");
+#endif
}
}
}
@@ -3493,11 +3835,12 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec
entry->downTime = downTime;
entry->pointerCount = pointerCount;
entry->firstSample.eventTime = eventTime;
+ entry->firstSample.eventTimeBeforeCoalescing = eventTime;
entry->firstSample.next = NULL;
entry->lastSample = & entry->firstSample;
for (uint32_t i = 0; i < pointerCount; i++) {
entry->pointerIds[i] = pointerIds[i];
- entry->firstSample.pointerCoords[i] = pointerCoords[i];
+ entry->firstSample.pointerCoords[i].copyFrom(pointerCoords[i]);
}
return entry;
}
@@ -3529,7 +3872,7 @@ void InputDispatcher::Allocator::releaseInjectionState(InjectionState* injection
if (injectionState->refCount == 0) {
mInjectionStatePool.free(injectionState);
} else {
- assert(injectionState->refCount > 0);
+ LOG_ASSERT(injectionState->refCount > 0);
}
}
@@ -3545,7 +3888,7 @@ void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
releaseMotionEntry(static_cast<MotionEntry*>(entry));
break;
default:
- assert(false);
+ LOG_ASSERT(false);
break;
}
}
@@ -3557,7 +3900,7 @@ void InputDispatcher::Allocator::releaseConfigurationChangedEntry(
releaseEventEntryInjectionState(entry);
mConfigurationChangeEntryPool.free(entry);
} else {
- assert(entry->refCount > 0);
+ LOG_ASSERT(entry->refCount > 0);
}
}
@@ -3567,7 +3910,7 @@ void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) {
releaseEventEntryInjectionState(entry);
mKeyEntryPool.free(entry);
} else {
- assert(entry->refCount > 0);
+ LOG_ASSERT(entry->refCount > 0);
}
}
@@ -3582,10 +3925,14 @@ void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
}
mMotionEntryPool.free(entry);
} else {
- assert(entry->refCount > 0);
+ LOG_ASSERT(entry->refCount > 0);
}
}
+void InputDispatcher::Allocator::freeMotionSample(MotionSample* sample) {
+ mMotionSamplePool.free(sample);
+}
+
void InputDispatcher::Allocator::releaseDispatchEntry(DispatchEntry* entry) {
releaseEventEntry(entry->eventEntry);
mDispatchEntryPool.free(entry);
@@ -3599,9 +3946,10 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
nsecs_t eventTime, const PointerCoords* pointerCoords) {
MotionSample* sample = mMotionSamplePool.alloc();
sample->eventTime = eventTime;
+ sample->eventTimeBeforeCoalescing = eventTime;
uint32_t pointerCount = motionEntry->pointerCount;
for (uint32_t i = 0; i < pointerCount; i++) {
- sample->pointerCoords[i] = pointerCoords[i];
+ sample->pointerCoords[i].copyFrom(pointerCoords[i]);
}
sample->next = NULL;
@@ -3628,6 +3976,21 @@ uint32_t InputDispatcher::MotionEntry::countSamples() const {
return count;
}
+bool InputDispatcher::MotionEntry::canAppendSamples(int32_t action, uint32_t pointerCount,
+ const int32_t* pointerIds) const {
+ if (this->action != action
+ || this->pointerCount != pointerCount
+ || this->isInjected()) {
+ return false;
+ }
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ if (this->pointerIds[i] != pointerIds[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
// --- InputDispatcher::InputState ---
@@ -3641,22 +4004,30 @@ bool InputDispatcher::InputState::isNeutral() const {
return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
}
-void InputDispatcher::InputState::trackEvent(
- const EventEntry* entry) {
+void InputDispatcher::InputState::trackEvent(const EventEntry* entry, int32_t action) {
switch (entry->type) {
case EventEntry::TYPE_KEY:
- trackKey(static_cast<const KeyEntry*>(entry));
+ trackKey(static_cast<const KeyEntry*>(entry), action);
break;
case EventEntry::TYPE_MOTION:
- trackMotion(static_cast<const MotionEntry*>(entry));
+ trackMotion(static_cast<const MotionEntry*>(entry), action);
break;
}
}
-void InputDispatcher::InputState::trackKey(
- const KeyEntry* entry) {
- int32_t action = entry->action;
+void InputDispatcher::InputState::trackKey(const KeyEntry* entry, int32_t action) {
+ if (action == AKEY_EVENT_ACTION_UP
+ && (entry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
+ for (size_t i = 0; i < mFallbackKeys.size(); ) {
+ if (mFallbackKeys.valueAt(i) == entry->keyCode) {
+ mFallbackKeys.removeItemsAt(i);
+ } else {
+ i += 1;
+ }
+ }
+ }
+
for (size_t i = 0; i < mKeyMementos.size(); i++) {
KeyMemento& memento = mKeyMementos.editItemAt(i);
if (memento.deviceId == entry->deviceId
@@ -3691,17 +4062,18 @@ Found:
}
}
-void InputDispatcher::InputState::trackMotion(
- const MotionEntry* entry) {
- int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK;
+void InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action) {
+ int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
for (size_t i = 0; i < mMotionMementos.size(); i++) {
MotionMemento& memento = mMotionMementos.editItemAt(i);
if (memento.deviceId == entry->deviceId
&& memento.source == entry->source) {
- switch (action) {
+ switch (actionMasked) {
case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
mMotionMementos.removeAt(i);
return;
@@ -3722,7 +4094,11 @@ void InputDispatcher::InputState::trackMotion(
}
Found:
- if (action == AMOTION_EVENT_ACTION_DOWN) {
+ switch (actionMasked) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
mMotionMementos.push();
MotionMemento& memento = mMotionMementos.editTop();
memento.deviceId = entry->deviceId;
@@ -3731,6 +4107,7 @@ Found:
memento.yPrecision = entry->yPrecision;
memento.downTime = entry->downTime;
memento.setPointers(entry);
+ memento.hovering = actionMasked != AMOTION_EVENT_ACTION_DOWN;
}
}
@@ -3738,13 +4115,13 @@ void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry*
pointerCount = entry->pointerCount;
for (uint32_t i = 0; i < entry->pointerCount; i++) {
pointerIds[i] = entry->pointerIds[i];
- pointerCoords[i] = entry->lastSample->pointerCoords[i];
+ pointerCoords[i].copyFrom(entry->lastSample->pointerCoords[i]);
}
}
void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
Allocator* allocator, Vector<EventEntry*>& outEvents,
- CancelationOptions options) {
+ const CancelationOptions& options) {
for (size_t i = 0; i < mKeyMementos.size(); ) {
const KeyMemento& memento = mKeyMementos.itemAt(i);
if (shouldCancelKey(memento, options)) {
@@ -3763,7 +4140,10 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim
if (shouldCancelMotion(memento, options)) {
outEvents.push(allocator->obtainMotionEntry(currentTime,
memento.deviceId, memento.source, 0,
- AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0,
+ memento.hovering
+ ? AMOTION_EVENT_ACTION_HOVER_EXIT
+ : AMOTION_EVENT_ACTION_CANCEL,
+ 0, 0, 0,
memento.xPrecision, memento.yPrecision, memento.downTime,
memento.pointerCount, memento.pointerIds, memento.pointerCoords));
mMotionMementos.removeAt(i);
@@ -3776,6 +4156,7 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim
void InputDispatcher::InputState::clear() {
mKeyMementos.clear();
mMotionMementos.clear();
+ mFallbackKeys.clear();
}
void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
@@ -3796,13 +4177,36 @@ void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
}
}
+int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) {
+ ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+ return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
+}
+
+void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode,
+ int32_t fallbackKeyCode) {
+ ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+ if (index >= 0) {
+ mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
+ } else {
+ mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
+ }
+}
+
+void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) {
+ mFallbackKeys.removeItem(originalKeyCode);
+}
+
bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
- CancelationOptions options) {
- switch (options) {
- case CANCEL_ALL_EVENTS:
- case CANCEL_NON_POINTER_EVENTS:
+ const CancelationOptions& options) {
+ if (options.keyCode != -1 && memento.keyCode != options.keyCode) {
+ return false;
+ }
+
+ switch (options.mode) {
+ case CancelationOptions::CANCEL_ALL_EVENTS:
+ case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
return true;
- case CANCEL_FALLBACK_EVENTS:
+ case CancelationOptions::CANCEL_FALLBACK_EVENTS:
return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
default:
return false;
@@ -3810,13 +4214,13 @@ bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
}
bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento,
- CancelationOptions options) {
- switch (options) {
- case CANCEL_ALL_EVENTS:
+ const CancelationOptions& options) {
+ switch (options.mode) {
+ case CancelationOptions::CANCEL_ALL_EVENTS:
return true;
- case CANCEL_POINTER_EVENTS:
+ case CancelationOptions::CANCEL_POINTER_EVENTS:
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
- case CANCEL_NON_POINTER_EVENTS:
+ case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
default:
return false;
@@ -3830,8 +4234,7 @@ InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle) :
status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
inputPublisher(inputChannel),
- lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX),
- originalKeyCodeForFallback(-1) {
+ lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
}
InputDispatcher::Connection::~Connection() {
@@ -3929,12 +4332,15 @@ void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window,
touchedWindow.channel = window->inputChannel;
}
-void InputDispatcher::TouchState::removeOutsideTouchWindows() {
+void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
for (size_t i = 0 ; i < windows.size(); ) {
- if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) {
- windows.removeAt(i);
- } else {
+ TouchedWindow& window = windows.editItemAt(i);
+ if (window.targetFlags & InputTarget::FLAG_DISPATCH_AS_IS) {
+ window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
+ window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
i += 1;
+ } else {
+ windows.removeAt(i);
}
}
}
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index dd89328..96ece32 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -86,20 +86,40 @@ enum {
struct InputTarget {
enum {
/* This flag indicates that the event is being delivered to a foreground application. */
- FLAG_FOREGROUND = 0x01,
-
- /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
- * of the area of this target and so should instead be delivered as an
- * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
- FLAG_OUTSIDE = 0x02,
+ FLAG_FOREGROUND = 1 << 0,
/* This flag indicates that the target of a MotionEvent is partly or wholly
* obscured by another visible window above it. The motion event should be
* delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
- FLAG_WINDOW_IS_OBSCURED = 0x04,
+ FLAG_WINDOW_IS_OBSCURED = 1 << 1,
/* This flag indicates that a motion event is being split across multiple windows. */
- FLAG_SPLIT = 0x08,
+ FLAG_SPLIT = 1 << 2,
+
+ /* This flag indicates that the event should be sent as is.
+ * Should always be set unless the event is to be transmuted. */
+ FLAG_DISPATCH_AS_IS = 1 << 8,
+
+ /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
+ * of the area of this target and so should instead be delivered as an
+ * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
+ FLAG_DISPATCH_AS_OUTSIDE = 1 << 9,
+
+ /* This flag indicates that a hover sequence is starting in the given window.
+ * The event is transmuted into ACTION_HOVER_ENTER. */
+ FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10,
+
+ /* This flag indicates that a hover event happened outside of a window which handled
+ * previous hover events, signifying the end of the current hover sequence for that
+ * window.
+ * The event is transmuted into ACTION_HOVER_ENTER. */
+ FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
+
+ /* Mask for all dispatch modes. */
+ FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS
+ | FLAG_DISPATCH_AS_OUTSIDE
+ | FLAG_DISPATCH_AS_HOVER_ENTER
+ | FLAG_DISPATCH_AS_HOVER_EXIT,
};
// The input channel to be targeted.
@@ -160,6 +180,13 @@ public:
*/
virtual int32_t getMaxEventsPerSecond() = 0;
+ /* Filters an input event.
+ * Return true to dispatch the event unmodified, false to consume the event.
+ * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
+ * to injectInputEvent.
+ */
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
+
/* Intercepts a key event immediately before queueing it.
* The policy can use this method as an opportunity to perform power management functions
* and early event preprocessing such as updating policy flags.
@@ -250,7 +277,8 @@ public:
* This method may be called on any thread (usually by the input manager).
*/
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags) = 0;
/* Sets the list of input windows.
*
@@ -270,6 +298,14 @@ public:
*/
virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
+ /* Sets whether input event filtering is enabled.
+ * When enabled, incoming input events are sent to the policy's filterInputEvent
+ * method instead of being dispatched. The filter is expected to use
+ * injectInputEvent to inject the events it would like to have dispatched.
+ * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
+ */
+ virtual void setInputFilterEnabled(bool enabled) = 0;
+
/* Transfers touch focus from the window associated with one channel to the
* window associated with the other channel.
*
@@ -329,11 +365,13 @@ public:
int32_t switchCode, int32_t switchValue, uint32_t policyFlags) ;
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags);
virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
virtual void setFocusedApplication(const InputApplication* inputApplication);
virtual void setInputDispatchMode(bool enabled, bool frozen);
+ virtual void setInputFilterEnabled(bool enabled);
virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
const sp<InputChannel>& toChannel);
@@ -375,7 +413,7 @@ private:
bool dispatchInProgress; // initially false, set to true while dispatching
- inline bool isInjected() { return injectionState != NULL; }
+ inline bool isInjected() const { return injectionState != NULL; }
};
struct ConfigurationChangedEntry : EventEntry {
@@ -405,7 +443,8 @@ private:
struct MotionSample {
MotionSample* next;
- nsecs_t eventTime;
+ nsecs_t eventTime; // may be updated during coalescing
+ nsecs_t eventTimeBeforeCoalescing; // not updated during coalescing
PointerCoords pointerCoords[MAX_POINTERS];
};
@@ -427,6 +466,10 @@ private:
MotionSample* lastSample;
uint32_t countSamples() const;
+
+ // Checks whether we can append samples, assuming the device id and source are the same.
+ bool canAppendSamples(int32_t action, uint32_t pointerCount,
+ const int32_t* pointerIds) const;
};
// Tracks the progress of dispatching a particular event to a particular connection.
@@ -572,6 +615,7 @@ private:
void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
void releaseKeyEntry(KeyEntry* entry);
void releaseMotionEntry(MotionEntry* entry);
+ void freeMotionSample(MotionSample* sample);
void releaseDispatchEntry(DispatchEntry* entry);
void releaseCommandEntry(CommandEntry* entry);
@@ -594,18 +638,32 @@ private:
void releaseEventEntryInjectionState(EventEntry* entry);
};
- /* Tracks dispatched key and motion event state so that cancelation events can be
- * synthesized when events are dropped. */
- class InputState {
- public:
- // Specifies the sources to cancel.
- enum CancelationOptions {
+ /* Specifies which events are to be canceled and why. */
+ struct CancelationOptions {
+ enum Mode {
CANCEL_ALL_EVENTS = 0,
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
CANCEL_FALLBACK_EVENTS = 3,
};
+ // The criterion to use to determine which events should be canceled.
+ Mode mode;
+
+ // Descriptive reason for the cancelation.
+ const char* reason;
+
+ // The specific keycode of the key event to cancel, or -1 to cancel any key event.
+ int32_t keyCode;
+
+ CancelationOptions(Mode mode, const char* reason) :
+ mode(mode), reason(reason), keyCode(-1) { }
+ };
+
+ /* Tracks dispatched key and motion event state so that cancelation events can be
+ * synthesized when events are dropped. */
+ class InputState {
+ public:
InputState();
~InputState();
@@ -613,17 +671,17 @@ private:
bool isNeutral() const;
// Records tracking information for an event that has just been published.
- void trackEvent(const EventEntry* entry);
+ void trackEvent(const EventEntry* entry, int32_t action);
// Records tracking information for a key event that has just been published.
- void trackKey(const KeyEntry* entry);
+ void trackKey(const KeyEntry* entry, int32_t action);
// Records tracking information for a motion event that has just been published.
- void trackMotion(const MotionEntry* entry);
+ void trackMotion(const MotionEntry* entry, int32_t action);
// Synthesizes cancelation events for the current state and resets the tracked state.
void synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator,
- Vector<EventEntry*>& outEvents, CancelationOptions options);
+ Vector<EventEntry*>& outEvents, const CancelationOptions& options);
// Clears the current state.
void clear();
@@ -631,6 +689,21 @@ private:
// Copies pointer-related parts of the input state to another instance.
void copyPointerStateTo(InputState& other) const;
+ // Gets the fallback key associated with a keycode.
+ // Returns -1 if none.
+ // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy.
+ int32_t getFallbackKey(int32_t originalKeyCode);
+
+ // Sets the fallback key for a particular keycode.
+ void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode);
+
+ // Removes the fallback key for a particular keycode.
+ void removeFallbackKey(int32_t originalKeyCode);
+
+ inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const {
+ return mFallbackKeys;
+ }
+
private:
struct KeyMemento {
int32_t deviceId;
@@ -650,17 +723,19 @@ private:
uint32_t pointerCount;
int32_t pointerIds[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
+ bool hovering;
void setPointers(const MotionEntry* entry);
};
Vector<KeyMemento> mKeyMementos;
Vector<MotionMemento> mMotionMementos;
+ KeyedVector<int32_t, int32_t> mFallbackKeys;
static bool shouldCancelKey(const KeyMemento& memento,
- CancelationOptions options);
+ const CancelationOptions& options);
static bool shouldCancelMotion(const MotionMemento& memento,
- CancelationOptions options);
+ const CancelationOptions& options);
};
/* Manages the dispatch state associated with a single input channel. */
@@ -687,7 +762,6 @@ private:
nsecs_t lastEventTime; // the time when the event was originally captured
nsecs_t lastDispatchTime; // the time when the last event was dispatched
- int32_t originalKeyCodeForFallback; // original keycode for fallback in progress, -1 if none
explicit Connection(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle);
@@ -738,6 +812,11 @@ private:
void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
nsecs_t* nextWakeupTime);
+ // Batches a new sample onto a motion entry.
+ // Assumes that the we have already checked that we can append samples.
+ void batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState,
+ const PointerCoords* pointerCoords, const char* eventDescription);
+
// Enqueues an inbound event. Returns true if mLooper->wake() should be called.
bool enqueueInboundEventLocked(EventEntry* entry);
@@ -817,6 +896,7 @@ private:
// Dispatch state.
bool mDispatchEnabled;
bool mDispatchFrozen;
+ bool mInputFilterEnabled;
Vector<InputWindow> mWindows;
@@ -844,7 +924,7 @@ private:
void reset();
void copyFrom(const TouchState& other);
void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds);
- void removeOutsideTouchWindows();
+ void filterNonAsIsTouchWindows();
const InputWindow* getFirstForegroundWindow();
};
@@ -887,6 +967,9 @@ private:
bool mInputTargetWaitTimeoutExpired;
sp<InputApplicationHandle> mInputTargetWaitApplication;
+ // Contains the last window which received a hover event.
+ const InputWindow* mLastHoverWindow;
+
// Finding targets for input events.
void resetTargetsLocked();
void commitTargetsLocked();
@@ -901,7 +984,8 @@ private:
int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
nsecs_t* nextWakeupTime);
int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
- nsecs_t* nextWakeupTime, bool* outConflictingPointerActions);
+ nsecs_t* nextWakeupTime, bool* outConflictingPointerActions,
+ const MotionSample** outSplitBatchAfterSample);
void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
BitSet32 pointerIds);
@@ -920,6 +1004,9 @@ private:
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample);
+ void enqueueDispatchEntryLocked(const sp<Connection>& connection,
+ EventEntry* eventEntry, const InputTarget* inputTarget,
+ bool resumeWithAppendedMotionSample, int32_t dispatchMode);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool handled);
@@ -929,11 +1016,11 @@ private:
static int handleReceiveCallback(int receiveFd, int events, void* data);
void synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CancelationOptions options, const char* reason);
+ const CancelationOptions& options);
void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- InputState::CancelationOptions options, const char* reason);
+ const CancelationOptions& options);
void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
- InputState::CancelationOptions options, const char* reason);
+ const CancelationOptions& options);
// Splitting motion events across windows.
MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 3029028..98b3526 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -33,6 +33,8 @@
// Log debug messages about pointer assignment calculations.
#define DEBUG_POINTER_ASSIGNMENT 0
+// Log debug messages about gesture detection.
+#define DEBUG_GESTURES 0
#include "InputReader.h"
@@ -54,6 +56,67 @@
namespace android {
+// --- Constants ---
+
+// Quiet time between certain gesture transitions.
+// Time to allow for all fingers or buttons to settle into a stable state before
+// starting a new gesture.
+static const nsecs_t QUIET_INTERVAL = 100 * 1000000; // 100 ms
+
+// The minimum speed that a pointer must travel for us to consider switching the active
+// touch pointer to it during a drag. This threshold is set to avoid switching due
+// to noise from a finger resting on the touch pad (perhaps just pressing it down).
+static const float DRAG_MIN_SWITCH_SPEED = 50.0f; // pixels per second
+
+// Tap gesture delay time.
+// The time between down and up must be less than this to be considered a tap.
+static const nsecs_t TAP_INTERVAL = 150 * 1000000; // 150 ms
+
+// Tap drag gesture delay time.
+// The time between up and the next up must be greater than this to be considered a
+// drag. Otherwise, the previous tap is finished and a new tap begins.
+static const nsecs_t TAP_DRAG_INTERVAL = 150 * 1000000; // 150 ms
+
+// The distance in pixels that the pointer is allowed to move from initial down
+// to up and still be called a tap.
+static const float TAP_SLOP = 10.0f; // 10 pixels
+
+// Time after the first touch points go down to settle on an initial centroid.
+// This is intended to be enough time to handle cases where the user puts down two
+// fingers at almost but not quite exactly the same time.
+static const nsecs_t MULTITOUCH_SETTLE_INTERVAL = 100 * 1000000; // 100ms
+
+// The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
+// both of the pointers are moving at least this fast.
+static const float MULTITOUCH_MIN_SPEED = 150.0f; // pixels per second
+
+// The transition from PRESS to SWIPE gesture mode can only occur when the
+// cosine of the angle between the two vectors is greater than or equal to than this value
+// which indicates that the vectors are oriented in the same direction.
+// When the vectors are oriented in the exactly same direction, the cosine is 1.0.
+// (In exactly opposite directions, the cosine is -1.0.)
+static const float SWIPE_TRANSITION_ANGLE_COSINE = 0.5f; // cosine of 45 degrees
+
+// The transition from PRESS to SWIPE gesture mode can only occur when the
+// fingers are no more than this far apart relative to the diagonal size of
+// the touch pad. For example, a ratio of 0.5 means that the fingers must be
+// no more than half the diagonal size of the touch pad apart.
+static const float SWIPE_MAX_WIDTH_RATIO = 0.333f; // 1/3
+
+// The gesture movement speed factor relative to the size of the display.
+// Movement speed applies when the fingers are moving in the same direction.
+// Without acceleration, a full swipe of the touch pad diagonal in movement mode
+// will cover this portion of the display diagonal.
+static const float GESTURE_MOVEMENT_SPEED_RATIO = 0.8f;
+
+// The gesture zoom speed factor relative to the size of the display.
+// Zoom speed applies when the fingers are mostly moving relative to each other
+// to execute a scale gesture or similar.
+// Without acceleration, a full swipe of the touch pad diagonal in zoom mode
+// will cover this portion of the display diagonal.
+static const float GESTURE_ZOOM_SPEED_RATIO = 0.3f;
+
+
// --- Static Functions ---
template<typename T>
@@ -77,8 +140,8 @@ inline static float avg(float x, float y) {
return (x + y) / 2;
}
-inline static float pythag(float x, float y) {
- return sqrtf(x * x + y * y);
+inline static float distance(float x1, float y1, float x2, float y2) {
+ return hypotf(x1 - x2, y1 - y2);
}
inline static int32_t signExtendNybble(int32_t value) {
@@ -183,6 +246,33 @@ static int32_t calculateEdgeFlagsUsingPointerBounds(
return edgeFlags;
}
+static void clampPositionUsingPointerBounds(
+ const sp<PointerControllerInterface>& pointerController, float* x, float* y) {
+ float minX, minY, maxX, maxY;
+ if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
+ if (*x < minX) {
+ *x = minX;
+ } else if (*x > maxX) {
+ *x = maxX;
+ }
+ if (*y < minY) {
+ *y = minY;
+ } else if (*y > maxY) {
+ *y = maxY;
+ }
+ }
+}
+
+static float calculateCommonVector(float a, float b) {
+ if (a > 0 && b > 0) {
+ return a < b ? a : b;
+ } else if (a < 0 && b < 0) {
+ return a > b ? a : b;
+ } else {
+ return 0;
+ }
+}
+
// --- InputReader ---
@@ -190,7 +280,7 @@ InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputDispatcherInterface>& dispatcher) :
mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
- mGlobalMetaState(0), mDisableVirtualKeysTimeout(-1) {
+ mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX) {
configureExcludedDevices();
updateGlobalMetaState();
updateInputConfiguration();
@@ -203,35 +293,61 @@ InputReader::~InputReader() {
}
void InputReader::loopOnce() {
- RawEvent rawEvent;
- mEventHub->getEvent(& rawEvent);
+ int32_t timeoutMillis = -1;
+ if (mNextTimeout != LLONG_MAX) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
+ }
+ size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
+ if (count) {
+ processEvents(mEventBuffer, count);
+ }
+ if (!count || timeoutMillis == 0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
#if DEBUG_RAW_EVENTS
- LOGD("Input event: device=%d type=0x%x scancode=%d keycode=%d value=%d",
- rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
- rawEvent.value);
+ LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
-
- process(& rawEvent);
+ mNextTimeout = LLONG_MAX;
+ timeoutExpired(now);
+ }
}
-void InputReader::process(const RawEvent* rawEvent) {
- switch (rawEvent->type) {
- case EventHubInterface::DEVICE_ADDED:
- addDevice(rawEvent->deviceId);
- break;
-
- case EventHubInterface::DEVICE_REMOVED:
- removeDevice(rawEvent->deviceId);
- break;
-
- case EventHubInterface::FINISHED_DEVICE_SCAN:
- handleConfigurationChanged(rawEvent->when);
- break;
-
- default:
- consumeEvent(rawEvent);
- break;
+void InputReader::processEvents(const RawEvent* rawEvents, size_t count) {
+ for (const RawEvent* rawEvent = rawEvents; count;) {
+ int32_t type = rawEvent->type;
+ size_t batchSize = 1;
+ if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+ int32_t deviceId = rawEvent->deviceId;
+ while (batchSize < count) {
+ if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
+ || rawEvent[batchSize].deviceId != deviceId) {
+ break;
+ }
+ batchSize += 1;
+ }
+#if DEBUG_RAW_EVENTS
+ LOGD("BatchSize: %d Count: %d", batchSize, count);
+#endif
+ processEventsForDevice(deviceId, rawEvent, batchSize);
+ } else {
+ switch (rawEvent->type) {
+ case EventHubInterface::DEVICE_ADDED:
+ addDevice(rawEvent->deviceId);
+ break;
+ case EventHubInterface::DEVICE_REMOVED:
+ removeDevice(rawEvent->deviceId);
+ break;
+ case EventHubInterface::FINISHED_DEVICE_SCAN:
+ handleConfigurationChanged(rawEvent->when);
+ break;
+ default:
+ LOG_ASSERT(false); // can't happen
+ break;
+ }
+ }
+ count -= batchSize;
+ rawEvent += batchSize;
}
}
@@ -352,9 +468,8 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui
return device;
}
-void InputReader::consumeEvent(const RawEvent* rawEvent) {
- int32_t deviceId = rawEvent->deviceId;
-
+void InputReader::processEventsForDevice(int32_t deviceId,
+ const RawEvent* rawEvents, size_t count) {
{ // acquire device registry reader lock
RWLock::AutoRLock _rl(mDeviceRegistryLock);
@@ -370,7 +485,20 @@ void InputReader::consumeEvent(const RawEvent* rawEvent) {
return;
}
- device->process(rawEvent);
+ device->process(rawEvents, count);
+ } // release device registry reader lock
+}
+
+void InputReader::timeoutExpired(nsecs_t when) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (!device->isIgnored()) {
+ device->timeoutExpired(when);
+ }
+ }
} // release device registry reader lock
}
@@ -484,6 +612,12 @@ void InputReader::fadePointer() {
} // release device registry reader lock
}
+void InputReader::requestTimeoutAtTime(nsecs_t when) {
+ if (when < mNextTimeout) {
+ mNextTimeout = when;
+ }
+}
+
void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
{ // acquire state lock
AutoMutex _l(mStateLock);
@@ -713,11 +847,33 @@ void InputDevice::reset() {
}
}
-void InputDevice::process(const RawEvent* rawEvent) {
+void InputDevice::process(const RawEvent* rawEvents, size_t count) {
+ // Process all of the events in order for each mapper.
+ // We cannot simply ask each mapper to process them in bulk because mappers may
+ // have side-effects that must be interleaved. For example, joystick movement events and
+ // gamepad button presses are handled by different mappers but they should be dispatched
+ // in the order received.
+ size_t numMappers = mMappers.size();
+ for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
+#if DEBUG_RAW_EVENTS
+ LOGD("Input event: device=%d type=0x%04x scancode=0x%04x "
+ "keycode=0x%04x value=0x%04x flags=0x%08x",
+ rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode,
+ rawEvent->value, rawEvent->flags);
+#endif
+
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->process(rawEvent);
+ }
+ }
+}
+
+void InputDevice::timeoutExpired(nsecs_t when) {
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
- mapper->process(rawEvent);
+ mapper->timeoutExpired(when);
}
}
@@ -812,6 +968,9 @@ void InputMapper::configure() {
void InputMapper::reset() {
}
+void InputMapper::timeoutExpired(nsecs_t when) {
+}
+
int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
return AKEY_STATE_UNKNOWN;
}
@@ -1268,7 +1427,7 @@ void CursorInputMapper::dumpParameters(String8& dump) {
dump.append(INDENT4 "Mode: navigation\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
dump.appendFormat(INDENT4 "OrientationAware: %s\n",
@@ -1443,10 +1602,32 @@ void CursorInputMapper::sync(nsecs_t when) {
motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
+ if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) {
+ vscroll = mAccumulator.relWheel;
+ } else {
+ vscroll = 0;
+ }
+ if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) {
+ hscroll = mAccumulator.relHWheel;
+ } else {
+ hscroll = 0;
+ }
+
if (mPointerController != NULL) {
- mPointerController->move(deltaX, deltaY);
- if (buttonsChanged) {
- mPointerController->setButtonState(mLocked.buttonState);
+ if (deltaX != 0 || deltaY != 0 || vscroll != 0 || hscroll != 0
+ || buttonsChanged) {
+ mPointerController->setPresentation(
+ PointerControllerInterface::PRESENTATION_POINTER);
+
+ if (deltaX != 0 || deltaY != 0) {
+ mPointerController->move(deltaX, deltaY);
+ }
+
+ if (buttonsChanged) {
+ mPointerController->setButtonState(mLocked.buttonState);
+ }
+
+ mPointerController->unfade();
}
float x, y;
@@ -1464,20 +1645,6 @@ void CursorInputMapper::sync(nsecs_t when) {
}
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
-
- if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) {
- vscroll = mAccumulator.relWheel;
- } else {
- vscroll = 0;
- }
- if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) {
- hscroll = mAccumulator.relHWheel;
- } else {
- hscroll = 0;
- }
- if (hscroll != 0 || vscroll != 0) {
- mPointerController->unfade();
- }
} // release lock
// Moving an external trackball or mouse should wake the device.
@@ -1495,8 +1662,15 @@ void CursorInputMapper::sync(nsecs_t when) {
motionEventAction, 0, metaState, motionEventEdgeFlags,
1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
- mAccumulator.clear();
+ // Send hover move after UP to tell the application that the mouse is hovering now.
+ if (motionEventAction == AMOTION_EVENT_ACTION_UP
+ && mPointerController != NULL) {
+ getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
+ }
+ // Send scroll events.
if (vscroll != 0 || hscroll != 0) {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
@@ -1505,6 +1679,8 @@ void CursorInputMapper::sync(nsecs_t when) {
AMOTION_EVENT_ACTION_SCROLL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
}
+
+ mAccumulator.clear();
}
int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
@@ -1540,7 +1716,7 @@ TouchInputMapper::~TouchInputMapper() {
}
uint32_t TouchInputMapper::getSources() {
- return mTouchSource;
+ return mTouchSource | mPointerSource;
}
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
@@ -1579,6 +1755,18 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
if (mLocked.orientedRanges.haveOrientation) {
info->addMotionRange(mLocked.orientedRanges.orientation);
}
+
+ if (mPointerController != NULL) {
+ float minX, minY, maxX, maxY;
+ if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_X, mPointerSource,
+ minX, maxX, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_Y, mPointerSource,
+ minY, maxY, 0.0f, 0.0f);
+ }
+ info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mPointerSource,
+ 0.0f, 1.0f, 0.0f, 0.0f);
+ }
} // release lock
}
@@ -1608,6 +1796,21 @@ void TouchInputMapper::dump(String8& dump) {
dump.appendFormat(INDENT3 "Last Touch:\n");
dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount);
+ dump.appendFormat(INDENT4 "Button State: 0x%08x\n", mLastTouch.buttonState);
+
+ if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+ dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n");
+ dump.appendFormat(INDENT4 "XMovementScale: %0.3f\n",
+ mLocked.pointerGestureXMovementScale);
+ dump.appendFormat(INDENT4 "YMovementScale: %0.3f\n",
+ mLocked.pointerGestureYMovementScale);
+ dump.appendFormat(INDENT4 "XZoomScale: %0.3f\n",
+ mLocked.pointerGestureXZoomScale);
+ dump.appendFormat(INDENT4 "YZoomScale: %0.3f\n",
+ mLocked.pointerGestureYZoomScale);
+ dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
+ mLocked.pointerGestureMaxSwipeWidth);
+ }
} // release lock
}
@@ -1630,6 +1833,8 @@ void TouchInputMapper::initializeLocked() {
mLocked.orientedRanges.haveTouchSize = false;
mLocked.orientedRanges.haveToolSize = false;
mLocked.orientedRanges.haveOrientation = false;
+
+ mPointerGesture.reset();
}
void TouchInputMapper::configure() {
@@ -1642,12 +1847,18 @@ void TouchInputMapper::configure() {
switch (mParameters.deviceType) {
case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
mTouchSource = AINPUT_SOURCE_TOUCHSCREEN;
+ mPointerSource = 0;
break;
case Parameters::DEVICE_TYPE_TOUCH_PAD:
mTouchSource = AINPUT_SOURCE_TOUCHPAD;
+ mPointerSource = 0;
+ break;
+ case Parameters::DEVICE_TYPE_POINTER:
+ mTouchSource = AINPUT_SOURCE_TOUCHPAD;
+ mPointerSource = AINPUT_SOURCE_MOUSE;
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
// Configure absolute axis information.
@@ -1671,14 +1882,30 @@ void TouchInputMapper::configureParameters() {
mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
mParameters.virtualKeyQuietTime = getPolicy()->getVirtualKeyQuietTime();
+ // TODO: Make this configurable.
+ //mParameters.gestureMode = Parameters::GESTURE_MODE_POINTER;
+ mParameters.gestureMode = Parameters::GESTURE_MODE_SPOTS;
+
+ if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X)
+ || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
+ // The device is a cursor device with a touch pad attached.
+ // By default don't use the touch pad to move the pointer.
+ mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ } else {
+ // The device is just a touch pad.
+ // By default use the touch pad to move the pointer and to perform related gestures.
+ mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ }
+
String8 deviceTypeString;
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
deviceTypeString)) {
if (deviceTypeString == "touchScreen") {
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
} else if (deviceTypeString == "touchPad") {
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ } else if (deviceTypeString == "pointer") {
+ mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
} else {
LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
}
@@ -1690,6 +1917,7 @@ void TouchInputMapper::configureParameters() {
mParameters.associatedDisplayId = mParameters.orientationAware
|| mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
+ || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
? 0 : -1;
}
@@ -1703,8 +1931,11 @@ void TouchInputMapper::dumpParameters(String8& dump) {
case Parameters::DEVICE_TYPE_TOUCH_PAD:
dump.append(INDENT4 "DeviceType: touchPad\n");
break;
+ case Parameters::DEVICE_TYPE_POINTER:
+ dump.append(INDENT4 "DeviceType: pointer\n");
+ break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
@@ -1776,6 +2007,11 @@ bool TouchInputMapper::configureSurfaceLocked() {
}
}
+ if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
+ && mPointerController == NULL) {
+ mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+ }
+
bool orientationChanged = mLocked.surfaceOrientation != orientation;
if (orientationChanged) {
mLocked.surfaceOrientation = orientation;
@@ -1808,7 +2044,7 @@ bool TouchInputMapper::configureSurfaceLocked() {
mLocked.geometricScale = avg(mLocked.xScale, mLocked.yScale);
// Size of diagonal axis.
- float diagonalSize = pythag(width, height);
+ float diagonalSize = hypotf(width, height);
// TouchMajor and TouchMinor factors.
if (mCalibration.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) {
@@ -1997,6 +2233,46 @@ bool TouchInputMapper::configureSurfaceLocked() {
mLocked.orientedRanges.y.fuzz = mLocked.yScale;
break;
}
+
+ // Compute pointer gesture detection parameters.
+ // TODO: These factors should not be hardcoded.
+ if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+ int32_t rawWidth = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1;
+ int32_t rawHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
+ float rawDiagonal = hypotf(rawWidth, rawHeight);
+ float displayDiagonal = hypotf(mLocked.associatedDisplayWidth,
+ mLocked.associatedDisplayHeight);
+
+ // Scale movements such that one whole swipe of the touch pad covers a
+ // given area relative to the diagonal size of the display.
+ // Assume that the touch pad has a square aspect ratio such that movements in
+ // X and Y of the same number of raw units cover the same physical distance.
+ const float scaleFactor = 0.8f;
+
+ mLocked.pointerGestureXMovementScale = GESTURE_MOVEMENT_SPEED_RATIO
+ * displayDiagonal / rawDiagonal;
+ mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale;
+
+ // Scale zooms to cover a smaller range of the display than movements do.
+ // This value determines the area around the pointer that is affected by freeform
+ // pointer gestures.
+ mLocked.pointerGestureXZoomScale = GESTURE_ZOOM_SPEED_RATIO
+ * displayDiagonal / rawDiagonal;
+ mLocked.pointerGestureYZoomScale = mLocked.pointerGestureXZoomScale;
+
+ // Max width between pointers to detect a swipe gesture is more than some fraction
+ // of the diagonal axis of the touch pad. Touches that are wider than this are
+ // translated into freeform gestures.
+ mLocked.pointerGestureMaxSwipeWidth = SWIPE_MAX_WIDTH_RATIO * rawDiagonal;
+
+ // Reset the current pointer gesture.
+ mPointerGesture.reset();
+
+ // Remove any current spots.
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerController->clearSpots();
+ }
+ }
}
return true;
@@ -2303,7 +2579,7 @@ void TouchInputMapper::dumpCalibration(String8& dump) {
dump.append(INDENT4 "touch.touchSize.calibration: pressure\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
// Tool Size
@@ -2321,7 +2597,7 @@ void TouchInputMapper::dumpCalibration(String8& dump) {
dump.append(INDENT4 "touch.toolSize.calibration: area\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
if (mCalibration.haveToolSizeLinearScale) {
@@ -2361,7 +2637,7 @@ void TouchInputMapper::dumpCalibration(String8& dump) {
dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
switch (mCalibration.pressureSource) {
@@ -2374,7 +2650,7 @@ void TouchInputMapper::dumpCalibration(String8& dump) {
case Calibration::PRESSURE_SOURCE_DEFAULT:
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
if (mCalibration.havePressureScale) {
@@ -2391,7 +2667,7 @@ void TouchInputMapper::dumpCalibration(String8& dump) {
dump.append(INDENT4 "touch.size.calibration: normalized\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
// Orientation
@@ -2406,7 +2682,7 @@ void TouchInputMapper::dumpCalibration(String8& dump) {
dump.append(INDENT4 "touch.orientation.calibration: vector\n");
break;
default:
- assert(false);
+ LOG_ASSERT(false);
}
}
@@ -2422,12 +2698,30 @@ void TouchInputMapper::reset() {
{ // acquire lock
AutoMutex _l(mLock);
initializeLocked();
+
+ if (mPointerController != NULL
+ && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerController->clearSpots();
+ }
} // release lock
InputMapper::reset();
}
void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
+#if DEBUG_RAW_EVENTS
+ if (!havePointerIds) {
+ LOGD("syncTouch: pointerCount=%d, no pointer ids", mCurrentTouch.pointerCount);
+ } else {
+ LOGD("syncTouch: pointerCount=%d, up=0x%08x, down=0x%08x, move=0x%08x, "
+ "last=0x%08x, current=0x%08x", mCurrentTouch.pointerCount,
+ mLastTouch.idBits.value & ~mCurrentTouch.idBits.value,
+ mCurrentTouch.idBits.value & ~mLastTouch.idBits.value,
+ mLastTouch.idBits.value & mCurrentTouch.idBits.value,
+ mLastTouch.idBits.value, mCurrentTouch.idBits.value);
+ }
+#endif
+
// Preprocess pointer data.
if (mParameters.useBadTouchFilter) {
if (applyBadTouchFilter()) {
@@ -2441,7 +2735,7 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
}
}
- if (! havePointerIds) {
+ if (!havePointerIds) {
calculatePointerIds();
}
@@ -2472,21 +2766,39 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
}
}
- // Process touches and virtual keys.
- TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
- if (touchResult == DISPATCH_TOUCH) {
- suppressSwipeOntoVirtualKeys(when);
- dispatchTouches(when, policyFlags);
+ TouchResult touchResult;
+ if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount == 0
+ && mLastTouch.buttonState == mCurrentTouch.buttonState) {
+ // Drop spurious syncs.
+ touchResult = DROP_STROKE;
+ } else {
+ // Process touches and virtual keys.
+ touchResult = consumeOffScreenTouches(when, policyFlags);
+ if (touchResult == DISPATCH_TOUCH) {
+ suppressSwipeOntoVirtualKeys(when);
+ if (mPointerController != NULL) {
+ dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
+ }
+ dispatchTouches(when, policyFlags);
+ }
}
// Copy current touch to last touch in preparation for the next cycle.
+ // Keep the button state so we can track edge-triggered button state changes.
if (touchResult == DROP_STROKE) {
mLastTouch.clear();
+ mLastTouch.buttonState = savedTouch->buttonState;
} else {
mLastTouch.copyFrom(*savedTouch);
}
}
+void TouchInputMapper::timeoutExpired(nsecs_t when) {
+ if (mPointerController != NULL) {
+ dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
+ }
+}
+
TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
nsecs_t when, uint32_t policyFlags) {
int32_t keyEventAction, keyEventFlags;
@@ -2629,329 +2941,1330 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
return; // nothing to do!
}
+ // Update current touch coordinates.
+ int32_t edgeFlags;
+ float xPrecision, yPrecision;
+ prepareTouches(&edgeFlags, &xPrecision, &yPrecision);
+
+ // Dispatch motions.
BitSet32 currentIdBits = mCurrentTouch.idBits;
BitSet32 lastIdBits = mLastTouch.idBits;
+ uint32_t metaState = getContext()->getGlobalMetaState();
if (currentIdBits == lastIdBits) {
// No pointer id changes so this is a move event.
// The dispatcher takes care of batching moves so we don't have to deal with that here.
- int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE;
- dispatchTouch(when, policyFlags, & mCurrentTouch,
- currentIdBits, -1, currentPointerCount, motionEventAction);
+ dispatchMotion(when, policyFlags, mTouchSource,
+ AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mCurrentTouchCoords, mCurrentTouch.idToIndex, currentIdBits, -1,
+ xPrecision, yPrecision, mDownTime);
} else {
// There may be pointers going up and pointers going down and pointers moving
// all at the same time.
- BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value);
- BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value);
- BitSet32 activeIdBits(lastIdBits.value);
- uint32_t pointerCount = lastPointerCount;
-
- // Produce an intermediate representation of the touch data that consists of the
- // old location of pointers that have just gone up and the new location of pointers that
- // have just moved but omits the location of pointers that have just gone down.
- TouchData interimTouch;
- interimTouch.copyFrom(mLastTouch);
-
+ BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);
+ BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);
BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
- bool moveNeeded = false;
- while (!moveIdBits.isEmpty()) {
- uint32_t moveId = moveIdBits.firstMarkedBit();
- moveIdBits.clearBit(moveId);
-
- int32_t oldIndex = mLastTouch.idToIndex[moveId];
- int32_t newIndex = mCurrentTouch.idToIndex[moveId];
- if (mLastTouch.pointers[oldIndex] != mCurrentTouch.pointers[newIndex]) {
- interimTouch.pointers[oldIndex] = mCurrentTouch.pointers[newIndex];
- moveNeeded = true;
- }
- }
+ BitSet32 dispatchedIdBits(lastIdBits.value);
+
+ // Update last coordinates of pointers that have moved so that we observe the new
+ // pointer positions at the same time as other pointers that have just gone up.
+ bool moveNeeded = updateMovedPointerCoords(
+ mCurrentTouchCoords, mCurrentTouch.idToIndex,
+ mLastTouchCoords, mLastTouch.idToIndex,
+ moveIdBits);
- // Dispatch pointer up events using the interim pointer locations.
+ // Dispatch pointer up events.
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.firstMarkedBit();
upIdBits.clearBit(upId);
- BitSet32 oldActiveIdBits = activeIdBits;
- activeIdBits.clearBit(upId);
- int32_t motionEventAction;
- if (activeIdBits.isEmpty()) {
- motionEventAction = AMOTION_EVENT_ACTION_UP;
- } else {
- motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP;
- }
-
- dispatchTouch(when, policyFlags, &interimTouch,
- oldActiveIdBits, upId, pointerCount, motionEventAction);
- pointerCount -= 1;
+ dispatchMotion(when, policyFlags, mTouchSource,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, 0,
+ mLastTouchCoords, mLastTouch.idToIndex, dispatchedIdBits, upId,
+ xPrecision, yPrecision, mDownTime);
+ dispatchedIdBits.clearBit(upId);
}
// Dispatch move events if any of the remaining pointers moved from their old locations.
// Although applications receive new locations as part of individual pointer up
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded) {
- dispatchTouch(when, policyFlags, &mCurrentTouch,
- activeIdBits, -1, pointerCount, AMOTION_EVENT_ACTION_MOVE);
+ LOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
+ dispatchMotion(when, policyFlags, mTouchSource,
+ AMOTION_EVENT_ACTION_MOVE, 0, metaState, 0,
+ mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, -1,
+ xPrecision, yPrecision, mDownTime);
}
// Dispatch pointer down events using the new pointer locations.
while (!downIdBits.isEmpty()) {
uint32_t downId = downIdBits.firstMarkedBit();
downIdBits.clearBit(downId);
- BitSet32 oldActiveIdBits = activeIdBits;
- activeIdBits.markBit(downId);
+ dispatchedIdBits.markBit(downId);
- int32_t motionEventAction;
- if (oldActiveIdBits.isEmpty()) {
- motionEventAction = AMOTION_EVENT_ACTION_DOWN;
+ if (dispatchedIdBits.count() == 1) {
+ // First pointer is going down. Set down time.
mDownTime = when;
} else {
- motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN;
+ // Only send edge flags with first pointer down.
+ edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
}
- pointerCount += 1;
- dispatchTouch(when, policyFlags, &mCurrentTouch,
- activeIdBits, downId, pointerCount, motionEventAction);
+ dispatchMotion(when, policyFlags, mTouchSource,
+ AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, edgeFlags,
+ mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, downId,
+ xPrecision, yPrecision, mDownTime);
}
}
+
+ // Update state for next time.
+ for (uint32_t i = 0; i < currentPointerCount; i++) {
+ mLastTouchCoords[i].copyFrom(mCurrentTouchCoords[i]);
+ }
}
-void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
- TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,
- int32_t motionEventAction) {
- int32_t pointerIds[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- int32_t motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
- float xPrecision, yPrecision;
+void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags,
+ float* outXPrecision, float* outYPrecision) {
+ uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+ uint32_t lastPointerCount = mLastTouch.pointerCount;
- { // acquire lock
- AutoMutex _l(mLock);
+ AutoMutex _l(mLock);
+
+ // Walk through the the active pointers and map touch screen coordinates (TouchData) into
+ // display or surface coordinates (PointerCoords) and adjust for display orientation.
+ for (uint32_t i = 0; i < currentPointerCount; i++) {
+ const PointerData& in = mCurrentTouch.pointers[i];
+
+ // ToolMajor and ToolMinor
+ float toolMajor, toolMinor;
+ switch (mCalibration.toolSizeCalibration) {
+ case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
+ toolMajor = in.toolMajor * mLocked.geometricScale;
+ if (mRawAxes.toolMinor.valid) {
+ toolMinor = in.toolMinor * mLocked.geometricScale;
+ } else {
+ toolMinor = toolMajor;
+ }
+ break;
+ case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
+ toolMajor = in.toolMajor != 0
+ ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias
+ : 0;
+ if (mRawAxes.toolMinor.valid) {
+ toolMinor = in.toolMinor != 0
+ ? in.toolMinor * mLocked.toolSizeLinearScale
+ + mLocked.toolSizeLinearBias
+ : 0;
+ } else {
+ toolMinor = toolMajor;
+ }
+ break;
+ case Calibration::TOOL_SIZE_CALIBRATION_AREA:
+ if (in.toolMajor != 0) {
+ float diameter = sqrtf(in.toolMajor
+ * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias);
+ toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias;
+ } else {
+ toolMajor = 0;
+ }
+ toolMinor = toolMajor;
+ break;
+ default:
+ toolMajor = 0;
+ toolMinor = 0;
+ break;
+ }
+
+ if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) {
+ toolMajor /= currentPointerCount;
+ toolMinor /= currentPointerCount;
+ }
+
+ // Pressure
+ float rawPressure;
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ rawPressure = in.pressure;
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ rawPressure = in.touchMajor;
+ break;
+ default:
+ rawPressure = 0;
+ }
+
+ float pressure;
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ pressure = rawPressure * mLocked.pressureScale;
+ break;
+ default:
+ pressure = 1;
+ break;
+ }
+
+ // TouchMajor and TouchMinor
+ float touchMajor, touchMinor;
+ switch (mCalibration.touchSizeCalibration) {
+ case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
+ touchMajor = in.touchMajor * mLocked.geometricScale;
+ if (mRawAxes.touchMinor.valid) {
+ touchMinor = in.touchMinor * mLocked.geometricScale;
+ } else {
+ touchMinor = touchMajor;
+ }
+ break;
+ case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE:
+ touchMajor = toolMajor * pressure;
+ touchMinor = toolMinor * pressure;
+ break;
+ default:
+ touchMajor = 0;
+ touchMinor = 0;
+ break;
+ }
+
+ if (touchMajor > toolMajor) {
+ touchMajor = toolMajor;
+ }
+ if (touchMinor > toolMinor) {
+ touchMinor = toolMinor;
+ }
+
+ // Size
+ float size;
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_NORMALIZED: {
+ float rawSize = mRawAxes.toolMinor.valid
+ ? avg(in.toolMajor, in.toolMinor)
+ : in.toolMajor;
+ size = rawSize * mLocked.sizeScale;
+ break;
+ }
+ default:
+ size = 0;
+ break;
+ }
+
+ // Orientation
+ float orientation;
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ orientation = in.orientation * mLocked.orientationScale;
+ break;
+ case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
+ int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
+ int32_t c2 = signExtendNybble(in.orientation & 0x0f);
+ if (c1 != 0 || c2 != 0) {
+ orientation = atan2f(c1, c2) * 0.5f;
+ float scale = 1.0f + hypotf(c1, c2) / 16.0f;
+ touchMajor *= scale;
+ touchMinor /= scale;
+ toolMajor *= scale;
+ toolMinor /= scale;
+ } else {
+ orientation = 0;
+ }
+ break;
+ }
+ default:
+ orientation = 0;
+ }
+
+ // X and Y
+ // Adjust coords for surface orientation.
+ float x, y;
+ switch (mLocked.surfaceOrientation) {
+ case DISPLAY_ORIENTATION_90:
+ x = float(in.y - mRawAxes.y.minValue) * mLocked.yScale;
+ y = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale;
+ orientation -= M_PI_2;
+ if (orientation < - M_PI_2) {
+ orientation += M_PI;
+ }
+ break;
+ case DISPLAY_ORIENTATION_180:
+ x = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale;
+ y = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale;
+ break;
+ case DISPLAY_ORIENTATION_270:
+ x = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale;
+ y = float(in.x - mRawAxes.x.minValue) * mLocked.xScale;
+ orientation += M_PI_2;
+ if (orientation > M_PI_2) {
+ orientation -= M_PI;
+ }
+ break;
+ default:
+ x = float(in.x - mRawAxes.x.minValue) * mLocked.xScale;
+ y = float(in.y - mRawAxes.y.minValue) * mLocked.yScale;
+ break;
+ }
+
+ // Write output coords.
+ PointerCoords& out = mCurrentTouchCoords[i];
+ out.clear();
+ out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
+ out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
+ }
+
+ // Check edge flags by looking only at the first pointer since the flags are
+ // global to the event.
+ *outEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
+ if (lastPointerCount == 0 && currentPointerCount > 0) {
+ const PointerData& in = mCurrentTouch.pointers[0];
+
+ if (in.x <= mRawAxes.x.minValue) {
+ *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT,
+ mLocked.surfaceOrientation);
+ } else if (in.x >= mRawAxes.x.maxValue) {
+ *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT,
+ mLocked.surfaceOrientation);
+ }
+ if (in.y <= mRawAxes.y.minValue) {
+ *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP,
+ mLocked.surfaceOrientation);
+ } else if (in.y >= mRawAxes.y.maxValue) {
+ *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM,
+ mLocked.surfaceOrientation);
+ }
+ }
+
+ *outXPrecision = mLocked.orientedXPrecision;
+ *outYPrecision = mLocked.orientedYPrecision;
+}
- // Walk through the the active pointers and map touch screen coordinates (TouchData) into
- // display or surface coordinates (PointerCoords) and adjust for display orientation.
- for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) {
+void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags,
+ bool isTimeout) {
+ // Switch pointer presentation.
+ mPointerController->setPresentation(
+ mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
+ ? PointerControllerInterface::PRESENTATION_SPOT
+ : PointerControllerInterface::PRESENTATION_POINTER);
+
+ // Update current gesture coordinates.
+ bool cancelPreviousGesture, finishPreviousGesture;
+ bool sendEvents = preparePointerGestures(when,
+ &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
+ if (!sendEvents) {
+ return;
+ }
+
+ // Show the pointer if needed.
+ if (mPointerGesture.currentGestureMode != PointerGesture::NEUTRAL
+ && mPointerGesture.currentGestureMode != PointerGesture::QUIET) {
+ mPointerController->unfade();
+ }
+
+ // Send events!
+ uint32_t metaState = getContext()->getGlobalMetaState();
+
+ // Update last coordinates of pointers that have moved so that we observe the new
+ // pointer positions at the same time as other pointers that have just gone up.
+ bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP
+ || mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG
+ || mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
+ || mPointerGesture.currentGestureMode == PointerGesture::PRESS
+ || mPointerGesture.currentGestureMode == PointerGesture::SWIPE
+ || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
+ bool moveNeeded = false;
+ if (down && !cancelPreviousGesture && !finishPreviousGesture
+ && !mPointerGesture.lastGestureIdBits.isEmpty()
+ && !mPointerGesture.currentGestureIdBits.isEmpty()) {
+ BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value
+ & mPointerGesture.lastGestureIdBits.value);
+ moveNeeded = updateMovedPointerCoords(
+ mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
+ movedGestureIdBits);
+ }
+
+ // Send motion events for all pointers that went up or were canceled.
+ BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
+ if (!dispatchedGestureIdBits.isEmpty()) {
+ if (cancelPreviousGesture) {
+ dispatchMotion(when, policyFlags, mPointerSource,
+ AMOTION_EVENT_ACTION_CANCEL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
+ dispatchedGestureIdBits, -1,
+ 0, 0, mPointerGesture.downTime);
+
+ dispatchedGestureIdBits.clear();
+ } else {
+ BitSet32 upGestureIdBits;
+ if (finishPreviousGesture) {
+ upGestureIdBits = dispatchedGestureIdBits;
+ } else {
+ upGestureIdBits.value = dispatchedGestureIdBits.value
+ & ~mPointerGesture.currentGestureIdBits.value;
+ }
+ while (!upGestureIdBits.isEmpty()) {
+ uint32_t id = upGestureIdBits.firstMarkedBit();
+ upGestureIdBits.clearBit(id);
+
+ dispatchMotion(when, policyFlags, mPointerSource,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0,
+ metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
+ dispatchedGestureIdBits, id,
+ 0, 0, mPointerGesture.downTime);
+
+ dispatchedGestureIdBits.clearBit(id);
+ }
+ }
+ }
+
+ // Send motion events for all pointers that moved.
+ if (moveNeeded) {
+ dispatchMotion(when, policyFlags, mPointerSource,
+ AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
+ dispatchedGestureIdBits, -1,
+ 0, 0, mPointerGesture.downTime);
+ }
+
+ // Send motion events for all pointers that went down.
+ if (down) {
+ BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value
+ & ~dispatchedGestureIdBits.value);
+ while (!downGestureIdBits.isEmpty()) {
+ uint32_t id = downGestureIdBits.firstMarkedBit();
+ downGestureIdBits.clearBit(id);
+ dispatchedGestureIdBits.markBit(id);
+
+ int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
+ if (dispatchedGestureIdBits.count() == 1) {
+ // First pointer is going down. Calculate edge flags and set down time.
+ uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+ const PointerCoords& downCoords = mPointerGesture.currentGestureCoords[index];
+ edgeFlags = calculateEdgeFlagsUsingPointerBounds(mPointerController,
+ downCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ downCoords.getAxisValue(AMOTION_EVENT_AXIS_Y));
+ mPointerGesture.downTime = when;
+ }
+
+ dispatchMotion(when, policyFlags, mPointerSource,
+ AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, edgeFlags,
+ mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
+ dispatchedGestureIdBits, id,
+ 0, 0, mPointerGesture.downTime);
+ }
+ }
+
+ // Send motion events for hover.
+ if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
+ dispatchMotion(when, policyFlags, mPointerSource,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.currentGestureIdBits, -1,
+ 0, 0, mPointerGesture.downTime);
+ }
+
+ // Update state.
+ mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode;
+ if (!down) {
+ mPointerGesture.lastGestureIdBits.clear();
+ } else {
+ mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits;
+ for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
- uint32_t inIndex = touch->idToIndex[id];
+ uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+ mPointerGesture.lastGestureCoords[index].copyFrom(
+ mPointerGesture.currentGestureCoords[index]);
+ mPointerGesture.lastGestureIdToIndex[id] = index;
+ }
+ }
+}
- const PointerData& in = touch->pointers[inIndex];
+bool TouchInputMapper::preparePointerGestures(nsecs_t when,
+ bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout) {
+ *outCancelPreviousGesture = false;
+ *outFinishPreviousGesture = false;
- // ToolMajor and ToolMinor
- float toolMajor, toolMinor;
- switch (mCalibration.toolSizeCalibration) {
- case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
- toolMajor = in.toolMajor * mLocked.geometricScale;
- if (mRawAxes.toolMinor.valid) {
- toolMinor = in.toolMinor * mLocked.geometricScale;
- } else {
- toolMinor = toolMajor;
+ AutoMutex _l(mLock);
+
+ // Handle TAP timeout.
+ if (isTimeout) {
+#if DEBUG_GESTURES
+ LOGD("Gestures: Processing timeout");
+#endif
+
+ if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+ if (when <= mPointerGesture.tapUpTime + TAP_DRAG_INTERVAL) {
+ // The tap/drag timeout has not yet expired.
+ getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime + TAP_DRAG_INTERVAL);
+ } else {
+ // The tap is finished.
+#if DEBUG_GESTURES
+ LOGD("Gestures: TAP finished");
+#endif
+ *outFinishPreviousGesture = true;
+
+ mPointerGesture.activeGestureId = -1;
+ mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureIdBits.clear();
+
+ mPointerController->setButtonState(0);
+
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
+ mPointerGesture.spotIdBits.clear();
+ moveSpotsLocked();
}
- break;
- case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
- toolMajor = in.toolMajor != 0
- ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias
- : 0;
- if (mRawAxes.toolMinor.valid) {
- toolMinor = in.toolMinor != 0
- ? in.toolMinor * mLocked.toolSizeLinearScale
- + mLocked.toolSizeLinearBias
- : 0;
- } else {
- toolMinor = toolMajor;
+ return true;
+ }
+ }
+
+ // We did not handle this timeout.
+ return false;
+ }
+
+ // Update the velocity tracker.
+ {
+ VelocityTracker::Position positions[MAX_POINTERS];
+ uint32_t count = 0;
+ for (BitSet32 idBits(mCurrentTouch.idBits); !idBits.isEmpty(); count++) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ uint32_t index = mCurrentTouch.idToIndex[id];
+ positions[count].x = mCurrentTouch.pointers[index].x
+ * mLocked.pointerGestureXMovementScale;
+ positions[count].y = mCurrentTouch.pointers[index].y
+ * mLocked.pointerGestureYMovementScale;
+ }
+ mPointerGesture.velocityTracker.addMovement(when, mCurrentTouch.idBits, positions);
+ }
+
+ // Pick a new active touch id if needed.
+ // Choose an arbitrary pointer that just went down, if there is one.
+ // Otherwise choose an arbitrary remaining pointer.
+ // This guarantees we always have an active touch id when there is at least one pointer.
+ // We keep the same active touch id for as long as possible.
+ bool activeTouchChanged = false;
+ int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
+ int32_t activeTouchId = lastActiveTouchId;
+ if (activeTouchId < 0) {
+ if (!mCurrentTouch.idBits.isEmpty()) {
+ activeTouchChanged = true;
+ activeTouchId = mPointerGesture.activeTouchId = mCurrentTouch.idBits.firstMarkedBit();
+ mPointerGesture.firstTouchTime = when;
+ }
+ } else if (!mCurrentTouch.idBits.hasBit(activeTouchId)) {
+ activeTouchChanged = true;
+ if (!mCurrentTouch.idBits.isEmpty()) {
+ activeTouchId = mPointerGesture.activeTouchId = mCurrentTouch.idBits.firstMarkedBit();
+ } else {
+ activeTouchId = mPointerGesture.activeTouchId = -1;
+ }
+ }
+
+ // Determine whether we are in quiet time.
+ bool isQuietTime = false;
+ if (activeTouchId < 0) {
+ mPointerGesture.resetQuietTime();
+ } else {
+ isQuietTime = when < mPointerGesture.quietTime + QUIET_INTERVAL;
+ if (!isQuietTime) {
+ if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS
+ || mPointerGesture.lastGestureMode == PointerGesture::SWIPE
+ || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM)
+ && mCurrentTouch.pointerCount < 2) {
+ // Enter quiet time when exiting swipe or freeform state.
+ // This is to prevent accidentally entering the hover state and flinging the
+ // pointer when finishing a swipe and there is still one pointer left onscreen.
+ isQuietTime = true;
+ } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
+ && mCurrentTouch.pointerCount >= 2
+ && !isPointerDown(mCurrentTouch.buttonState)) {
+ // Enter quiet time when releasing the button and there are still two or more
+ // fingers down. This may indicate that one finger was used to press the button
+ // but it has not gone up yet.
+ isQuietTime = true;
+ }
+ if (isQuietTime) {
+ mPointerGesture.quietTime = when;
+ }
+ }
+ }
+
+ // Switch states based on button and pointer state.
+ if (isQuietTime) {
+ // Case 1: Quiet time. (QUIET)
+#if DEBUG_GESTURES
+ LOGD("Gestures: QUIET for next %0.3fms",
+ (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f);
+#endif
+ *outFinishPreviousGesture = true;
+
+ mPointerGesture.activeGestureId = -1;
+ mPointerGesture.currentGestureMode = PointerGesture::QUIET;
+ mPointerGesture.currentGestureIdBits.clear();
+
+ mPointerController->setButtonState(0);
+
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
+ mPointerGesture.spotIdBits.clear();
+ moveSpotsLocked();
+ }
+ } else if (isPointerDown(mCurrentTouch.buttonState)) {
+ // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
+ // The pointer follows the active touch point.
+ // Emit DOWN, MOVE, UP events at the pointer location.
+ //
+ // Only the active touch matters; other fingers are ignored. This policy helps
+ // to handle the case where the user places a second finger on the touch pad
+ // to apply the necessary force to depress an integrated button below the surface.
+ // We don't want the second finger to be delivered to applications.
+ //
+ // For this to work well, we need to make sure to track the pointer that is really
+ // active. If the user first puts one finger down to click then adds another
+ // finger to drag then the active pointer should switch to the finger that is
+ // being dragged.
+#if DEBUG_GESTURES
+ LOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
+ "currentTouchPointerCount=%d", activeTouchId, mCurrentTouch.pointerCount);
+#endif
+ // Reset state when just starting.
+ if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
+ *outFinishPreviousGesture = true;
+ mPointerGesture.activeGestureId = 0;
+ }
+
+ // Switch pointers if needed.
+ // Find the fastest pointer and follow it.
+ if (activeTouchId >= 0) {
+ if (mCurrentTouch.pointerCount > 1) {
+ int32_t bestId = -1;
+ float bestSpeed = DRAG_MIN_SWITCH_SPEED;
+ for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
+ uint32_t id = mCurrentTouch.pointers[i].id;
+ float vx, vy;
+ if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
+ float speed = hypotf(vx, vy);
+ if (speed > bestSpeed) {
+ bestId = id;
+ bestSpeed = speed;
+ }
+ }
}
- break;
- case Calibration::TOOL_SIZE_CALIBRATION_AREA:
- if (in.toolMajor != 0) {
- float diameter = sqrtf(in.toolMajor
- * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias);
- toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias;
- } else {
- toolMajor = 0;
+ if (bestId >= 0 && bestId != activeTouchId) {
+ mPointerGesture.activeTouchId = activeTouchId = bestId;
+ activeTouchChanged = true;
+#if DEBUG_GESTURES
+ LOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
+ "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed);
+#endif
}
- toolMinor = toolMajor;
- break;
- default:
- toolMajor = 0;
- toolMinor = 0;
- break;
}
- if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) {
- toolMajor /= pointerCount;
- toolMinor /= pointerCount;
+ if (mLastTouch.idBits.hasBit(activeTouchId)) {
+ const PointerData& currentPointer =
+ mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]];
+ const PointerData& lastPointer =
+ mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]];
+ float deltaX = (currentPointer.x - lastPointer.x)
+ * mLocked.pointerGestureXMovementScale;
+ float deltaY = (currentPointer.y - lastPointer.y)
+ * mLocked.pointerGestureYMovementScale;
+
+ // Move the pointer using a relative motion.
+ // When using spots, the click will occur at the position of the anchor
+ // spot and all other spots will move there.
+ mPointerController->move(deltaX, deltaY);
}
+ }
- // Pressure
- float rawPressure;
- switch (mCalibration.pressureSource) {
- case Calibration::PRESSURE_SOURCE_PRESSURE:
- rawPressure = in.pressure;
- break;
- case Calibration::PRESSURE_SOURCE_TOUCH:
- rawPressure = in.touchMajor;
- break;
- default:
- rawPressure = 0;
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+
+ mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+
+ mPointerController->setButtonState(BUTTON_STATE_PRIMARY);
+
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ if (activeTouchId >= 0) {
+ // Collapse all spots into one point at the pointer location.
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_DRAG;
+ mPointerGesture.spotIdBits.clear();
+ for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
+ uint32_t id = mCurrentTouch.pointers[i].id;
+ mPointerGesture.spotIdBits.markBit(id);
+ mPointerGesture.spotIdToIndex[id] = i;
+ mPointerGesture.spotCoords[i] = mPointerGesture.currentGestureCoords[0];
+ }
+ } else {
+ // No fingers. Generate a spot at the pointer location so the
+ // anchor appears to be pressed.
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_CLICK;
+ mPointerGesture.spotIdBits.clear();
+ mPointerGesture.spotIdBits.markBit(0);
+ mPointerGesture.spotIdToIndex[0] = 0;
+ mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
}
+ moveSpotsLocked();
+ }
+ } else if (mCurrentTouch.pointerCount == 0) {
+ // Case 3. No fingers down and button is not pressed. (NEUTRAL)
+ *outFinishPreviousGesture = true;
+
+ // Watch for taps coming out of HOVER or TAP_DRAG mode.
+ bool tapped = false;
+ if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER
+ || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG)
+ && mLastTouch.pointerCount == 1) {
+ if (when <= mPointerGesture.tapDownTime + TAP_INTERVAL) {
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+ if (fabs(x - mPointerGesture.tapX) <= TAP_SLOP
+ && fabs(y - mPointerGesture.tapY) <= TAP_SLOP) {
+#if DEBUG_GESTURES
+ LOGD("Gestures: TAP");
+#endif
- float pressure;
- switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
- pressure = rawPressure * mLocked.pressureScale;
- break;
- default:
- pressure = 1;
- break;
- }
+ mPointerGesture.tapUpTime = when;
+ getContext()->requestTimeoutAtTime(when + TAP_DRAG_INTERVAL);
+
+ mPointerGesture.activeGestureId = 0;
+ mPointerGesture.currentGestureMode = PointerGesture::TAP;
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(
+ mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[
+ mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(
+ AMOTION_EVENT_AXIS_X, mPointerGesture.tapX);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(
+ AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(
+ AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+
+ mPointerController->setButtonState(BUTTON_STATE_PRIMARY);
+
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_TAP;
+ mPointerGesture.spotIdBits.clear();
+ mPointerGesture.spotIdBits.markBit(lastActiveTouchId);
+ mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0;
+ mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
+ moveSpotsLocked();
+ }
- // TouchMajor and TouchMinor
- float touchMajor, touchMinor;
- switch (mCalibration.touchSizeCalibration) {
- case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
- touchMajor = in.touchMajor * mLocked.geometricScale;
- if (mRawAxes.touchMinor.valid) {
- touchMinor = in.touchMinor * mLocked.geometricScale;
+ tapped = true;
} else {
- touchMinor = touchMajor;
+#if DEBUG_GESTURES
+ LOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f",
+ x - mPointerGesture.tapX,
+ y - mPointerGesture.tapY);
+#endif
}
- break;
- case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE:
- touchMajor = toolMajor * pressure;
- touchMinor = toolMinor * pressure;
- break;
- default:
- touchMajor = 0;
- touchMinor = 0;
- break;
+ } else {
+#if DEBUG_GESTURES
+ LOGD("Gestures: Not a TAP, %0.3fms since down",
+ (when - mPointerGesture.tapDownTime) * 0.000001f);
+#endif
}
+ }
+
+ if (!tapped) {
+#if DEBUG_GESTURES
+ LOGD("Gestures: NEUTRAL");
+#endif
+ mPointerGesture.activeGestureId = -1;
+ mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureIdBits.clear();
+
+ mPointerController->setButtonState(0);
- if (touchMajor > toolMajor) {
- touchMajor = toolMajor;
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
+ mPointerGesture.spotIdBits.clear();
+ moveSpotsLocked();
}
- if (touchMinor > toolMinor) {
- touchMinor = toolMinor;
+ }
+ } else if (mCurrentTouch.pointerCount == 1) {
+ // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
+ // The pointer follows the active touch point.
+ // When in HOVER, emit HOVER_MOVE events at the pointer location.
+ // When in TAP_DRAG, emit MOVE events at the pointer location.
+ LOG_ASSERT(activeTouchId >= 0);
+
+ mPointerGesture.currentGestureMode = PointerGesture::HOVER;
+ if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+ if (when <= mPointerGesture.tapUpTime + TAP_DRAG_INTERVAL) {
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+ if (fabs(x - mPointerGesture.tapX) <= TAP_SLOP
+ && fabs(y - mPointerGesture.tapY) <= TAP_SLOP) {
+ mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+ } else {
+#if DEBUG_GESTURES
+ LOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
+ x - mPointerGesture.tapX,
+ y - mPointerGesture.tapY);
+#endif
+ }
+ } else {
+#if DEBUG_GESTURES
+ LOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
+ (when - mPointerGesture.tapUpTime) * 0.000001f);
+#endif
}
+ } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
+ mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+ }
- // Size
- float size;
- switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_NORMALIZED: {
- float rawSize = mRawAxes.toolMinor.valid
- ? avg(in.toolMajor, in.toolMinor)
- : in.toolMajor;
- size = rawSize * mLocked.sizeScale;
- break;
+ if (mLastTouch.idBits.hasBit(activeTouchId)) {
+ const PointerData& currentPointer =
+ mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]];
+ const PointerData& lastPointer =
+ mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]];
+ float deltaX = (currentPointer.x - lastPointer.x)
+ * mLocked.pointerGestureXMovementScale;
+ float deltaY = (currentPointer.y - lastPointer.y)
+ * mLocked.pointerGestureYMovementScale;
+
+ // Move the pointer using a relative motion.
+ // When using spots, the hover or drag will occur at the position of the anchor spot.
+ mPointerController->move(deltaX, deltaY);
+ }
+
+ bool down;
+ if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
+#if DEBUG_GESTURES
+ LOGD("Gestures: TAP_DRAG");
+#endif
+ down = true;
+ } else {
+#if DEBUG_GESTURES
+ LOGD("Gestures: HOVER");
+#endif
+ *outFinishPreviousGesture = true;
+ mPointerGesture.activeGestureId = 0;
+ down = false;
+ }
+
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+ down ? 1.0f : 0.0f);
+
+ mPointerController->setButtonState(down ? BUTTON_STATE_PRIMARY : 0);
+
+ if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
+ mPointerGesture.resetTap();
+ mPointerGesture.tapDownTime = when;
+ mPointerGesture.tapX = x;
+ mPointerGesture.tapY = y;
+ }
+
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotGesture = down ? PointerControllerInterface::SPOT_GESTURE_DRAG
+ : PointerControllerInterface::SPOT_GESTURE_HOVER;
+ mPointerGesture.spotIdBits.clear();
+ mPointerGesture.spotIdBits.markBit(activeTouchId);
+ mPointerGesture.spotIdToIndex[activeTouchId] = 0;
+ mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
+ moveSpotsLocked();
+ }
+ } else {
+ // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
+ // We need to provide feedback for each finger that goes down so we cannot wait
+ // for the fingers to move before deciding what to do.
+ //
+ // The ambiguous case is deciding what to do when there are two fingers down but they
+ // have not moved enough to determine whether they are part of a drag or part of a
+ // freeform gesture, or just a press or long-press at the pointer location.
+ //
+ // When there are two fingers we start with the PRESS hypothesis and we generate a
+ // down at the pointer location.
+ //
+ // When the two fingers move enough or when additional fingers are added, we make
+ // a decision to transition into SWIPE or FREEFORM mode accordingly.
+ LOG_ASSERT(activeTouchId >= 0);
+
+ bool needReference = false;
+ bool settled = when >= mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL;
+ if (mPointerGesture.lastGestureMode != PointerGesture::PRESS
+ && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
+ && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+ *outFinishPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+ mPointerGesture.activeGestureId = 0;
+
+ if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
+ && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) {
+ // The spot is already visible and has settled, use it as the reference point
+ // for the gesture. Other spots will be positioned relative to this one.
+#if DEBUG_GESTURES
+ LOGD("Gestures: Using active spot as reference for MULTITOUCH, "
+ "settle time expired %0.3fms ago",
+ (when - mPointerGesture.firstTouchTime - MULTITOUCH_SETTLE_INTERVAL)
+ * 0.000001f);
+#endif
+ const PointerData& d = mLastTouch.pointers[mLastTouch.idToIndex[
+ mPointerGesture.activeTouchId]];
+ mPointerGesture.referenceTouchX = d.x;
+ mPointerGesture.referenceTouchY = d.y;
+ const PointerCoords& c = mPointerGesture.spotCoords[mPointerGesture.spotIdToIndex[
+ mPointerGesture.activeTouchId]];
+ mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X);
+ mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
+ } else {
+#if DEBUG_GESTURES
+ LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+ "settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
+ * 0.000001f);
+#endif
+ needReference = true;
}
- default:
- size = 0;
- break;
+ } else if (!settled && mCurrentTouch.pointerCount > mLastTouch.pointerCount) {
+ // Additional pointers have gone down but not yet settled.
+ // Reset the gesture.
+#if DEBUG_GESTURES
+ LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
+ "settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
+ * 0.000001f);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+ mPointerGesture.activeGestureId = 0;
+ } else {
+ // Continue previous gesture.
+ mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
+ }
+
+ if (needReference) {
+ // Use the centroid and pointer location as the reference points for the gesture.
+ mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
+ &mPointerGesture.referenceTouchY);
+ mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+ &mPointerGesture.referenceGestureY);
+ }
+
+ if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
+ float d;
+ if (mCurrentTouch.pointerCount > 2) {
+ // There are more than two pointers, switch to FREEFORM.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+ mCurrentTouch.pointerCount);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ } else if (((d = distance(
+ mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
+ mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
+ > mLocked.pointerGestureMaxSwipeWidth)) {
+ // There are two pointers but they are too far apart, switch to FREEFORM.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+ d, mLocked.pointerGestureMaxSwipeWidth);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ } else {
+ // There are two pointers. Wait for both pointers to start moving
+ // before deciding whether this is a SWIPE or FREEFORM gesture.
+ uint32_t id1 = mCurrentTouch.pointers[0].id;
+ uint32_t id2 = mCurrentTouch.pointers[1].id;
+
+ float vx1, vy1, vx2, vy2;
+ mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1);
+ mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2);
+
+ float speed1 = hypotf(vx1, vy1);
+ float speed2 = hypotf(vx2, vy2);
+ if (speed1 >= MULTITOUCH_MIN_SPEED && speed2 >= MULTITOUCH_MIN_SPEED) {
+ // Calculate the dot product of the velocity vectors.
+ // When the vectors are oriented in approximately the same direction,
+ // the angle betweeen them is near zero and the cosine of the angle
+ // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
+ float dot = vx1 * vx2 + vy1 * vy2;
+ float cosine = dot / (speed1 * speed2); // denominator always > 0
+ if (cosine >= SWIPE_TRANSITION_ANGLE_COSINE) {
+ // Pointers are moving in the same direction. Switch to SWIPE.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS transitioned to SWIPE, "
+ "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
+ "cosine %0.3f >= %0.3f",
+ speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
+ cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+#endif
+ mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+ } else {
+ // Pointers are moving in different directions. Switch to FREEFORM.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS transitioned to FREEFORM, "
+ "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
+ "cosine %0.3f < %0.3f",
+ speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
+ cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ }
+ }
+ }
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+ // Switch from SWIPE to FREEFORM if additional pointers go down.
+ // Cancel previous gesture.
+ if (mCurrentTouch.pointerCount > 2) {
+#if DEBUG_GESTURES
+ LOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+ mCurrentTouch.pointerCount);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
}
+ }
- // Orientation
- float orientation;
- switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
- orientation = in.orientation * mLocked.orientationScale;
- break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
- int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
- int32_t c2 = signExtendNybble(in.orientation & 0x0f);
- if (c1 != 0 || c2 != 0) {
- orientation = atan2f(c1, c2) * 0.5f;
- float scale = 1.0f + pythag(c1, c2) / 16.0f;
- touchMajor *= scale;
- touchMinor /= scale;
- toolMajor *= scale;
- toolMinor /= scale;
+ // Move the reference points based on the overall group motion of the fingers.
+ // The objective is to calculate a vector delta that is common to the movement
+ // of all fingers.
+ BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
+ if (!commonIdBits.isEmpty()) {
+ float commonDeltaX = 0, commonDeltaY = 0;
+ for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
+ bool first = (idBits == commonIdBits);
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+
+ const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
+ const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
+ float deltaX = cpd.x - lpd.x;
+ float deltaY = cpd.y - lpd.y;
+
+ if (first) {
+ commonDeltaX = deltaX;
+ commonDeltaY = deltaY;
} else {
- orientation = 0;
+ commonDeltaX = calculateCommonVector(commonDeltaX, deltaX);
+ commonDeltaY = calculateCommonVector(commonDeltaY, deltaY);
}
- break;
- }
- default:
- orientation = 0;
}
- // X and Y
- // Adjust coords for surface orientation.
- float x, y;
- switch (mLocked.surfaceOrientation) {
- case DISPLAY_ORIENTATION_90:
- x = float(in.y - mRawAxes.y.minValue) * mLocked.yScale;
- y = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale;
- orientation -= M_PI_2;
- if (orientation < - M_PI_2) {
- orientation += M_PI;
+ mPointerGesture.referenceTouchX += commonDeltaX;
+ mPointerGesture.referenceTouchY += commonDeltaY;
+ mPointerGesture.referenceGestureX +=
+ commonDeltaX * mLocked.pointerGestureXMovementScale;
+ mPointerGesture.referenceGestureY +=
+ commonDeltaY * mLocked.pointerGestureYMovementScale;
+ clampPositionUsingPointerBounds(mPointerController,
+ &mPointerGesture.referenceGestureX,
+ &mPointerGesture.referenceGestureY);
+ }
+
+ // Report gestures.
+ if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
+ // PRESS mode.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, mCurrentTouch.pointerCount);
+#endif
+ LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.referenceGestureX);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.referenceGestureY);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+
+ mPointerController->setButtonState(BUTTON_STATE_PRIMARY);
+
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_PRESS;
+ }
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+ // SWIPE mode.
+#if DEBUG_GESTURES
+ LOGD("Gestures: SWIPE activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, mCurrentTouch.pointerCount);
+#endif
+ LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.referenceGestureX);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.referenceGestureY);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+
+ mPointerController->setButtonState(0); // touch is not actually following the pointer
+
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_SWIPE;
+ }
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+ // FREEFORM mode.
+#if DEBUG_GESTURES
+ LOGD("Gestures: FREEFORM activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, mCurrentTouch.pointerCount);
+#endif
+ LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+
+ BitSet32 mappedTouchIdBits;
+ BitSet32 usedGestureIdBits;
+ if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+ // Initially, assign the active gesture id to the active touch point
+ // if there is one. No other touch id bits are mapped yet.
+ if (!*outCancelPreviousGesture) {
+ mappedTouchIdBits.markBit(activeTouchId);
+ usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
+ mPointerGesture.activeGestureId;
+ } else {
+ mPointerGesture.activeGestureId = -1;
}
- break;
- case DISPLAY_ORIENTATION_180:
- x = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale;
- y = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale;
- break;
- case DISPLAY_ORIENTATION_270:
- x = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale;
- y = float(in.x - mRawAxes.x.minValue) * mLocked.xScale;
- orientation += M_PI_2;
- if (orientation > M_PI_2) {
- orientation -= M_PI;
+ } else {
+ // Otherwise, assume we mapped all touches from the previous frame.
+ // Reuse all mappings that are still applicable.
+ mappedTouchIdBits.value = mLastTouch.idBits.value & mCurrentTouch.idBits.value;
+ usedGestureIdBits = mPointerGesture.lastGestureIdBits;
+
+ // Check whether we need to choose a new active gesture id because the
+ // current went went up.
+ for (BitSet32 upTouchIdBits(mLastTouch.idBits.value & ~mCurrentTouch.idBits.value);
+ !upTouchIdBits.isEmpty(); ) {
+ uint32_t upTouchId = upTouchIdBits.firstMarkedBit();
+ upTouchIdBits.clearBit(upTouchId);
+ uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
+ if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
+ mPointerGesture.activeGestureId = -1;
+ break;
+ }
}
- break;
- default:
- x = float(in.x - mRawAxes.x.minValue) * mLocked.xScale;
- y = float(in.y - mRawAxes.y.minValue) * mLocked.yScale;
- break;
}
- // Write output coords.
- PointerCoords& out = pointerCoords[outIndex];
- out.clear();
- out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
- out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
- out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
- out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
-
- pointerIds[outIndex] = int32_t(id);
-
- if (id == changedId) {
- motionEventAction |= outIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+#if DEBUG_GESTURES
+ LOGD("Gestures: FREEFORM follow up "
+ "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
+ "activeGestureId=%d",
+ mappedTouchIdBits.value, usedGestureIdBits.value,
+ mPointerGesture.activeGestureId);
+#endif
+
+ for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
+ uint32_t touchId = mCurrentTouch.pointers[i].id;
+ uint32_t gestureId;
+ if (!mappedTouchIdBits.hasBit(touchId)) {
+ gestureId = usedGestureIdBits.firstUnmarkedBit();
+ usedGestureIdBits.markBit(gestureId);
+ mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
+#if DEBUG_GESTURES
+ LOGD("Gestures: FREEFORM "
+ "new mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+#endif
+ } else {
+ gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
+#if DEBUG_GESTURES
+ LOGD("Gestures: FREEFORM "
+ "existing mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+#endif
+ }
+ mPointerGesture.currentGestureIdBits.markBit(gestureId);
+ mPointerGesture.currentGestureIdToIndex[gestureId] = i;
+
+ float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX)
+ * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX;
+ float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY)
+ * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY;
+
+ mPointerGesture.currentGestureCoords[i].clear();
+ mPointerGesture.currentGestureCoords[i].setAxisValue(
+ AMOTION_EVENT_AXIS_X, x);
+ mPointerGesture.currentGestureCoords[i].setAxisValue(
+ AMOTION_EVENT_AXIS_Y, y);
+ mPointerGesture.currentGestureCoords[i].setAxisValue(
+ AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+ }
+
+ if (mPointerGesture.activeGestureId < 0) {
+ mPointerGesture.activeGestureId =
+ mPointerGesture.currentGestureIdBits.firstMarkedBit();
+#if DEBUG_GESTURES
+ LOGD("Gestures: FREEFORM new "
+ "activeGestureId=%d", mPointerGesture.activeGestureId);
+#endif
}
- }
- // Check edge flags by looking only at the first pointer since the flags are
- // global to the event.
- if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
- uint32_t inIndex = touch->idToIndex[pointerIds[0]];
- const PointerData& in = touch->pointers[inIndex];
+ mPointerController->setButtonState(0); // touch is not actually following the pointer
- if (in.x <= mRawAxes.x.minValue) {
- motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT,
- mLocked.surfaceOrientation);
- } else if (in.x >= mRawAxes.x.maxValue) {
- motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT,
- mLocked.surfaceOrientation);
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_FREEFORM;
}
- if (in.y <= mRawAxes.y.minValue) {
- motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP,
- mLocked.surfaceOrientation);
- } else if (in.y >= mRawAxes.y.maxValue) {
- motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM,
- mLocked.surfaceOrientation);
+ }
+
+ // Update spot locations for PRESS, SWIPE and FREEFORM.
+ // We use the same calculation as we do to calculate the gesture pointers
+ // for FREEFORM so that the spots smoothly track gestures.
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerGesture.spotIdBits.clear();
+ for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
+ uint32_t id = mCurrentTouch.pointers[i].id;
+ mPointerGesture.spotIdBits.markBit(id);
+ mPointerGesture.spotIdToIndex[id] = i;
+
+ float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX)
+ * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX;
+ float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY)
+ * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY;
+
+ mPointerGesture.spotCoords[i].clear();
+ mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
}
+ moveSpotsLocked();
}
+ }
- xPrecision = mLocked.orientedXPrecision;
- yPrecision = mLocked.orientedYPrecision;
- } // release lock
+#if DEBUG_GESTURES
+ LOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
+ "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
+ "lastGestureMode=%d, lastGestureIdBits=0x%08x",
+ toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
+ mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
+ mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
+ for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+ const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
+ LOGD(" currentGesture[%d]: index=%d, x=%0.3f, y=%0.3f, pressure=%0.3f",
+ id, index, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ }
+ for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
+ const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
+ LOGD(" lastGesture[%d]: index=%d, x=%0.3f, y=%0.3f, pressure=%0.3f",
+ id, index, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ }
+#endif
+ return true;
+}
- getDispatcher()->notifyMotion(when, getDeviceId(), mTouchSource, policyFlags,
- motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
- pointerCount, pointerIds, pointerCoords,
- xPrecision, yPrecision, mDownTime);
+void TouchInputMapper::moveSpotsLocked() {
+ mPointerController->setSpots(mPointerGesture.spotGesture,
+ mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex, mPointerGesture.spotIdBits);
+}
+
+void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
+ int32_t action, int32_t flags, uint32_t metaState, int32_t edgeFlags,
+ const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
+ int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) {
+ PointerCoords pointerCoords[MAX_POINTERS];
+ int32_t pointerIds[MAX_POINTERS];
+ uint32_t pointerCount = 0;
+ while (!idBits.isEmpty()) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ uint32_t index = idToIndex[id];
+ pointerIds[pointerCount] = id;
+ pointerCoords[pointerCount].copyFrom(coords[index]);
+
+ if (changedId >= 0 && id == uint32_t(changedId)) {
+ action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ }
+
+ pointerCount += 1;
+ }
+
+ LOG_ASSERT(pointerCount != 0);
+
+ if (changedId >= 0 && pointerCount == 1) {
+ // Replace initial down and final up action.
+ // We can compare the action without masking off the changed pointer index
+ // because we know the index is 0.
+ if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ action = AMOTION_EVENT_ACTION_DOWN;
+ } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
+ action = AMOTION_EVENT_ACTION_UP;
+ } else {
+ // Can't happen.
+ LOG_ASSERT(false);
+ }
+ }
+
+ getDispatcher()->notifyMotion(when, getDeviceId(), source, policyFlags,
+ action, flags, metaState, edgeFlags,
+ pointerCount, pointerIds, pointerCoords, xPrecision, yPrecision, downTime);
+}
+
+bool TouchInputMapper::updateMovedPointerCoords(
+ const PointerCoords* inCoords, const uint32_t* inIdToIndex,
+ PointerCoords* outCoords, const uint32_t* outIdToIndex, BitSet32 idBits) const {
+ bool changed = false;
+ while (!idBits.isEmpty()) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+
+ uint32_t inIndex = inIdToIndex[id];
+ uint32_t outIndex = outIdToIndex[id];
+ const PointerCoords& curInCoords = inCoords[inIndex];
+ PointerCoords& curOutCoords = outCoords[outIndex];
+
+ if (curInCoords != curOutCoords) {
+ curOutCoords.copyFrom(curInCoords);
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+void TouchInputMapper::fadePointer() {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ if (mPointerController != NULL) {
+ mPointerController->fade();
+ }
+ } // release lock
}
bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) {
@@ -3080,7 +4393,7 @@ void TouchInputMapper::calculatePointerIds() {
// Previous iterations consumed the root element of the heap.
// Pop root element off of the heap (sift down).
heapSize -= 1;
- assert(heapSize > 0);
+ LOG_ASSERT(heapSize > 0);
// Sift down.
heap[0] = heap[heapSize];
@@ -3592,6 +4905,7 @@ void SingleTouchInputMapper::initialize() {
mY = 0;
mPressure = 0; // default to 0 for devices that don't report pressure
mToolWidth = 0; // default to 0 for devices that don't report tool width
+ mButtonState = 0;
}
void SingleTouchInputMapper::reset() {
@@ -3611,6 +4925,19 @@ void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
// not have received valid position information yet. This logic assumes that
// BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
break;
+ default:
+ if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+ uint32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode);
+ if (buttonState) {
+ if (rawEvent->value) {
+ mAccumulator.buttonDown |= buttonState;
+ } else {
+ mAccumulator.buttonUp |= buttonState;
+ }
+ mAccumulator.fields |= Accumulator::FIELD_BUTTONS;
+ }
+ }
+ break;
}
break;
@@ -3671,6 +4998,10 @@ void SingleTouchInputMapper::sync(nsecs_t when) {
mToolWidth = mAccumulator.absToolWidth;
}
+ if (fields & Accumulator::FIELD_BUTTONS) {
+ mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp;
+ }
+
mCurrentTouch.clear();
if (mDown) {
@@ -3686,6 +5017,7 @@ void SingleTouchInputMapper::sync(nsecs_t when) {
mCurrentTouch.pointers[0].orientation = 0;
mCurrentTouch.idToIndex[0] = 0;
mCurrentTouch.idBits.markBit(0);
+ mCurrentTouch.buttonState = mButtonState;
}
syncTouch(when, true);
@@ -3715,6 +5047,7 @@ MultiTouchInputMapper::~MultiTouchInputMapper() {
void MultiTouchInputMapper::initialize() {
mAccumulator.clear();
+ mButtonState = 0;
}
void MultiTouchInputMapper::reset() {
@@ -3725,6 +5058,20 @@ void MultiTouchInputMapper::reset() {
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
+ case EV_KEY: {
+ if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+ uint32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode);
+ if (buttonState) {
+ if (rawEvent->value) {
+ mAccumulator.buttonDown |= buttonState;
+ } else {
+ mAccumulator.buttonUp |= buttonState;
+ }
+ }
+ }
+ break;
+ }
+
case EV_ABS: {
uint32_t pointerIndex = mAccumulator.pointerCount;
Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex];
@@ -3902,6 +5249,9 @@ void MultiTouchInputMapper::sync(nsecs_t when) {
mCurrentTouch.pointerCount = outCount;
+ mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp;
+ mCurrentTouch.buttonState = mButtonState;
+
syncTouch(when, havePointerIds);
mAccumulator.clear();
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 68002ca..0485617 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -159,6 +159,8 @@ public:
virtual void fadePointer() = 0;
+ virtual void requestTimeoutAtTime(nsecs_t when) = 0;
+
virtual InputReaderPolicyInterface* getPolicy() = 0;
virtual InputDispatcherInterface* getDispatcher() = 0;
virtual EventHubInterface* getEventHub() = 0;
@@ -214,6 +216,10 @@ private:
virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); }
virtual EventHubInterface* getEventHub() { return mEventHub.get(); }
+ // The event queue.
+ static const int EVENT_BUFFER_SIZE = 256;
+ RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
+
// This reader/writer lock guards the list of input devices.
// The writer lock must be held whenever the list of input devices is modified
// and then promptly released.
@@ -226,15 +232,15 @@ private:
KeyedVector<int32_t, InputDevice*> mDevices;
// low-level input event decoding and device management
- void process(const RawEvent* rawEvent);
+ void processEvents(const RawEvent* rawEvents, size_t count);
void addDevice(int32_t deviceId);
void removeDevice(int32_t deviceId);
- void configureExcludedDevices();
-
- void consumeEvent(const RawEvent* rawEvent);
+ void processEventsForDevice(int32_t deviceId, const RawEvent* rawEvents, size_t count);
+ void timeoutExpired(nsecs_t when);
void handleConfigurationChanged(nsecs_t when);
+ void configureExcludedDevices();
// state management for all devices
Mutex mStateLock;
@@ -248,11 +254,14 @@ private:
InputConfiguration mInputConfiguration;
void updateInputConfiguration();
- nsecs_t mDisableVirtualKeysTimeout;
+ nsecs_t mDisableVirtualKeysTimeout; // only accessed by reader thread
virtual void disableVirtualKeysUntil(nsecs_t time);
virtual bool shouldDropVirtualKey(nsecs_t now,
InputDevice* device, int32_t keyCode, int32_t scanCode);
+ nsecs_t mNextTimeout; // only accessed by reader thread
+ virtual void requestTimeoutAtTime(nsecs_t when);
+
// state queries
typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
@@ -295,7 +304,8 @@ public:
void addMapper(InputMapper* mapper);
void configure();
void reset();
- void process(const RawEvent* rawEvent);
+ void process(const RawEvent* rawEvents, size_t count);
+ void timeoutExpired(nsecs_t when);
void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
@@ -352,6 +362,7 @@ public:
virtual void configure();
virtual void reset();
virtual void process(const RawEvent* rawEvent) = 0;
+ virtual void timeoutExpired(nsecs_t when);
virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
@@ -558,6 +569,9 @@ public:
virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
+ virtual void fadePointer();
+ virtual void timeoutExpired(nsecs_t when);
+
protected:
Mutex mLock;
@@ -611,10 +625,12 @@ protected:
PointerData pointers[MAX_POINTERS];
BitSet32 idBits;
uint32_t idToIndex[MAX_POINTER_ID + 1];
+ uint32_t buttonState;
void copyFrom(const TouchData& other) {
pointerCount = other.pointerCount;
idBits = other.idBits;
+ buttonState = other.buttonState;
for (uint32_t i = 0; i < pointerCount; i++) {
pointers[i] = other.pointers[i];
@@ -627,17 +643,34 @@ protected:
inline void clear() {
pointerCount = 0;
idBits.clear();
+ buttonState = 0;
+ }
+
+ void getCentroid(float* outX, float* outY) {
+ float x = 0, y = 0;
+ if (pointerCount != 0) {
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ x += pointers[i].x;
+ y += pointers[i].y;
+ }
+ x /= pointerCount;
+ y /= pointerCount;
+ }
+ *outX = x;
+ *outY = y;
}
};
// Input sources supported by the device.
uint32_t mTouchSource; // sources when reporting touch data
+ uint32_t mPointerSource; // sources when reporting pointer gestures
// Immutable configuration parameters.
struct Parameters {
enum DeviceType {
DEVICE_TYPE_TOUCH_SCREEN,
DEVICE_TYPE_TOUCH_PAD,
+ DEVICE_TYPE_POINTER,
};
DeviceType deviceType;
@@ -648,6 +681,12 @@ protected:
bool useJumpyTouchFilter;
bool useAveragingTouchFilter;
nsecs_t virtualKeyQuietTime;
+
+ enum GestureMode {
+ GESTURE_MODE_POINTER,
+ GESTURE_MODE_SPOTS,
+ };
+ GestureMode gestureMode;
} mParameters;
// Immutable calibration parameters in parsed form.
@@ -735,11 +774,17 @@ protected:
// Current and previous touch sample data.
TouchData mCurrentTouch;
+ PointerCoords mCurrentTouchCoords[MAX_POINTERS];
+
TouchData mLastTouch;
+ PointerCoords mLastTouchCoords[MAX_POINTERS];
// The time the primary pointer last went down.
nsecs_t mDownTime;
+ // The pointer controller, or null if the device is not a pointer.
+ sp<PointerControllerInterface> mPointerController;
+
struct LockedState {
Vector<VirtualKey> virtualKeys;
@@ -804,6 +849,17 @@ protected:
int32_t keyCode;
int32_t scanCode;
} currentVirtualKey;
+
+ // Scale factor for gesture based pointer movements.
+ float pointerGestureXMovementScale;
+ float pointerGestureYMovementScale;
+
+ // Scale factor for gesture based zooming and other freeform motions.
+ float pointerGestureXZoomScale;
+ float pointerGestureYZoomScale;
+
+ // The maximum swipe width.
+ float pointerGestureMaxSwipeWidth;
} mLocked;
virtual void configureParameters();
@@ -869,13 +925,158 @@ private:
uint64_t distance : 48; // squared distance
};
+ struct PointerGesture {
+ enum Mode {
+ // No fingers, button is not pressed.
+ // Nothing happening.
+ NEUTRAL,
+
+ // No fingers, button is not pressed.
+ // Tap detected.
+ // Emits DOWN and UP events at the pointer location.
+ TAP,
+
+ // Exactly one finger dragging following a tap.
+ // Pointer follows the active finger.
+ // Emits DOWN, MOVE and UP events at the pointer location.
+ TAP_DRAG,
+
+ // Button is pressed.
+ // Pointer follows the active finger if there is one. Other fingers are ignored.
+ // Emits DOWN, MOVE and UP events at the pointer location.
+ BUTTON_CLICK_OR_DRAG,
+
+ // Exactly one finger, button is not pressed.
+ // Pointer follows the active finger.
+ // Emits HOVER_MOVE events at the pointer location.
+ HOVER,
+
+ // Exactly two fingers but neither have moved enough to clearly indicate
+ // whether a swipe or freeform gesture was intended. We consider the
+ // pointer to be pressed so this enables clicking or long-pressing on buttons.
+ // Pointer does not move.
+ // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate.
+ PRESS,
+
+ // Exactly two fingers moving in the same direction, button is not pressed.
+ // Pointer does not move.
+ // Emits DOWN, MOVE and UP events with a single pointer coordinate that
+ // follows the midpoint between both fingers.
+ SWIPE,
+
+ // Two or more fingers moving in arbitrary directions, button is not pressed.
+ // Pointer does not move.
+ // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow
+ // each finger individually relative to the initial centroid of the finger.
+ FREEFORM,
+
+ // Waiting for quiet time to end before starting the next gesture.
+ QUIET,
+ };
+
+ // Time the first finger went down.
+ nsecs_t firstTouchTime;
+
+ // The active pointer id from the raw touch data.
+ int32_t activeTouchId; // -1 if none
+
+ // The active pointer id from the gesture last delivered to the application.
+ int32_t activeGestureId; // -1 if none
+
+ // Pointer coords and ids for the current and previous pointer gesture.
+ Mode currentGestureMode;
+ BitSet32 currentGestureIdBits;
+ uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
+ PointerCoords currentGestureCoords[MAX_POINTERS];
+
+ Mode lastGestureMode;
+ BitSet32 lastGestureIdBits;
+ uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
+ PointerCoords lastGestureCoords[MAX_POINTERS];
+
+ // Pointer coords and ids for the current spots.
+ PointerControllerInterface::SpotGesture spotGesture;
+ BitSet32 spotIdBits; // same set of ids as touch ids
+ uint32_t spotIdToIndex[MAX_POINTER_ID + 1];
+ PointerCoords spotCoords[MAX_POINTERS];
+
+ // Time the pointer gesture last went down.
+ nsecs_t downTime;
+
+ // Time when the pointer went down for a TAP.
+ nsecs_t tapDownTime;
+
+ // Time when the pointer went up for a TAP.
+ nsecs_t tapUpTime;
+
+ // Location of initial tap.
+ float tapX, tapY;
+
+ // Time we started waiting for quiescence.
+ nsecs_t quietTime;
+
+ // Reference points for multitouch gestures.
+ float referenceTouchX; // reference touch X/Y coordinates in surface units
+ float referenceTouchY;
+ float referenceGestureX; // reference gesture X/Y coordinates in pixels
+ float referenceGestureY;
+
+ // Describes how touch ids are mapped to gesture ids for freeform gestures.
+ uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1];
+
+ // A velocity tracker for determining whether to switch active pointers during drags.
+ VelocityTracker velocityTracker;
+
+ void reset() {
+ firstTouchTime = LLONG_MIN;
+ activeTouchId = -1;
+ activeGestureId = -1;
+ currentGestureMode = NEUTRAL;
+ currentGestureIdBits.clear();
+ lastGestureMode = NEUTRAL;
+ lastGestureIdBits.clear();
+ spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
+ spotIdBits.clear();
+ downTime = 0;
+ velocityTracker.clear();
+ resetTap();
+ resetQuietTime();
+ }
+
+ void resetTap() {
+ tapDownTime = LLONG_MIN;
+ tapUpTime = LLONG_MIN;
+ }
+
+ void resetQuietTime() {
+ quietTime = LLONG_MIN;
+ }
+ } mPointerGesture;
+
void initializeLocked();
TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags);
void dispatchTouches(nsecs_t when, uint32_t policyFlags);
- void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch,
- BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,
- int32_t motionEventAction);
+ void prepareTouches(int32_t* outEdgeFlags, float* outXPrecision, float* outYPrecision);
+ void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
+ bool preparePointerGestures(nsecs_t when,
+ bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout);
+ void moveSpotsLocked();
+
+ // Dispatches a motion event.
+ // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
+ // method will take care of setting the index and transmuting the action to DOWN or UP
+ // it is the first / last pointer to go down / up.
+ void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
+ int32_t action, int32_t flags, uint32_t metaState, int32_t edgeFlags,
+ const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
+ int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
+
+ // Updates pointer coords for pointers with specified ids that have moved.
+ // Returns true if any of them changed.
+ bool updateMovedPointerCoords(const PointerCoords* inCoords, const uint32_t* inIdToIndex,
+ PointerCoords* outCoords, const uint32_t* outIdToIndex, BitSet32 idBits) const;
+
void suppressSwipeOntoVirtualKeys(nsecs_t when);
bool isPointInsideSurfaceLocked(int32_t x, int32_t y);
@@ -907,6 +1108,7 @@ private:
FIELD_ABS_Y = 4,
FIELD_ABS_PRESSURE = 8,
FIELD_ABS_TOOL_WIDTH = 16,
+ FIELD_BUTTONS = 32,
};
uint32_t fields;
@@ -917,8 +1119,13 @@ private:
int32_t absPressure;
int32_t absToolWidth;
+ uint32_t buttonDown;
+ uint32_t buttonUp;
+
inline void clear() {
fields = 0;
+ buttonDown = 0;
+ buttonUp = 0;
}
} mAccumulator;
@@ -927,6 +1134,7 @@ private:
int32_t mY;
int32_t mPressure;
int32_t mToolWidth;
+ uint32_t mButtonState;
void initialize();
@@ -978,12 +1186,20 @@ private:
}
} pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
+ // Bitfield of buttons that went down or up.
+ uint32_t buttonDown;
+ uint32_t buttonUp;
+
inline void clear() {
pointerCount = 0;
pointers[0].clear();
+ buttonDown = 0;
+ buttonUp = 0;
}
} mAccumulator;
+ uint32_t mButtonState;
+
void initialize();
void sync(nsecs_t when);
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 72f1773..cde7294 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -114,10 +114,12 @@ struct InputWindow {
TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
- TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+14,
+ TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15,
TYPE_DRAG = FIRST_SYSTEM_WINDOW+16,
- TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+17,
+ TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17,
+ TYPE_POINTER = FIRST_SYSTEM_WINDOW+18,
+ TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19,
LAST_SYSTEM_WINDOW = 2999,
};
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index a4ee295..ffef720 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -36,59 +36,63 @@ namespace android {
// --- PointerController ---
// Time to wait before starting the fade when the pointer is inactive.
-static const nsecs_t INACTIVITY_FADE_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
-static const nsecs_t INACTIVITY_FADE_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
+static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
+static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
+
+// Time to wait between animation frames.
+static const nsecs_t ANIMATION_FRAME_INTERVAL = 1000000000LL / 60;
+
+// Time to spend fading out the spot completely.
+static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
// Time to spend fading out the pointer completely.
-static const nsecs_t FADE_DURATION = 500 * 1000000LL; // 500 ms
+static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
-// Time to wait between frames.
-static const nsecs_t FADE_FRAME_INTERVAL = 1000000000LL / 60;
-// Amount to subtract from alpha per frame.
-static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION;
+// --- PointerController ---
+PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
+ mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
+ mHandler = new WeakMessageHandler(this);
-PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLayer) :
- mLooper(looper), mPointerLayer(pointerLayer) {
AutoMutex _l(mLock);
+ mLocked.animationPending = false;
+
mLocked.displayWidth = -1;
mLocked.displayHeight = -1;
mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
- mLocked.pointerX = 0;
- mLocked.pointerY = 0;
- mLocked.buttonState = 0;
+ mLocked.presentation = PRESENTATION_POINTER;
+ mLocked.presentationChanged = false;
- mLocked.iconBitmap = NULL;
- mLocked.iconHotSpotX = 0;
- mLocked.iconHotSpotY = 0;
+ mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
- mLocked.fadeAlpha = 1;
- mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL;
+ mLocked.pointerIsFading = true; // keep the pointer initially faded
+ mLocked.pointerX = 0;
+ mLocked.pointerY = 0;
+ mLocked.pointerAlpha = 0.0f;
+ mLocked.pointerSprite = mSpriteController->createSprite();
+ mLocked.pointerIconChanged = false;
- mLocked.wantVisible = false;
- mLocked.visible = false;
- mLocked.drawn = false;
+ mLocked.buttonState = 0;
- mHandler = new WeakMessageHandler(this);
+ loadResources();
}
PointerController::~PointerController() {
mLooper->removeMessages(mHandler);
- if (mSurfaceControl != NULL) {
- mSurfaceControl->clear();
- mSurfaceControl.clear();
- }
+ AutoMutex _l(mLock);
- if (mSurfaceComposerClient != NULL) {
- mSurfaceComposerClient->dispose();
- mSurfaceComposerClient.clear();
- }
+ mLocked.pointerSprite.clear();
- delete mLocked.iconBitmap;
+ for (size_t i = 0; i < mLocked.spots.size(); i++) {
+ delete mLocked.spots.itemAt(i);
+ }
+ mLocked.spots.clear();
+ mLocked.recycledSprites.clear();
}
bool PointerController::getBounds(float* outMinX, float* outMinY,
@@ -141,8 +145,6 @@ void PointerController::setButtonState(uint32_t buttonState) {
if (mLocked.buttonState != buttonState) {
mLocked.buttonState = buttonState;
- unfadeBeforeUpdateLocked();
- updateLocked();
}
}
@@ -178,8 +180,7 @@ void PointerController::setPositionLocked(float x, float y) {
} else {
mLocked.pointerY = y;
}
- unfadeBeforeUpdateLocked();
- updateLocked();
+ updatePointerLocked();
}
}
@@ -193,95 +194,104 @@ void PointerController::getPosition(float* outX, float* outY) const {
void PointerController::fade() {
AutoMutex _l(mLock);
- startFadeLocked();
+ sendImmediateInactivityTimeoutLocked();
}
void PointerController::unfade() {
AutoMutex _l(mLock);
- if (unfadeBeforeUpdateLocked()) {
- updateLocked();
+ // Always reset the inactivity timer.
+ resetInactivityTimeoutLocked();
+
+ // Unfade immediately if needed.
+ if (mLocked.pointerIsFading) {
+ mLocked.pointerIsFading = false;
+ mLocked.pointerAlpha = 1.0f;
+ updatePointerLocked();
}
}
-void PointerController::setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay) {
+void PointerController::setPresentation(Presentation presentation) {
AutoMutex _l(mLock);
- if (mLocked.inactivityFadeDelay != inactivityFadeDelay) {
- mLocked.inactivityFadeDelay = inactivityFadeDelay;
- startInactivityFadeDelayLocked();
- }
-}
-
-void PointerController::updateLocked() {
- bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap;
+ if (mLocked.presentation != presentation) {
+ mLocked.presentation = presentation;
+ mLocked.presentationChanged = true;
- if (wantVisibleAndHavePointerIcon) {
- // Want the pointer to be visible.
- // Ensure the surface is created and drawn.
- if (!createSurfaceIfNeededLocked() || !drawPointerIfNeededLocked()) {
- return;
- }
- } else {
- // Don't want the pointer to be visible.
- // If it is not visible then we are done.
- if (mSurfaceControl == NULL || !mLocked.visible) {
- return;
+ if (presentation != PRESENTATION_SPOT) {
+ fadeOutAndReleaseAllSpotsLocked();
}
+
+ updatePointerLocked();
}
+}
- status_t status = mSurfaceComposerClient->openTransaction();
- if (status) {
- LOGE("Error opening surface transaction to update pointer surface.");
- return;
+void PointerController::setSpots(SpotGesture spotGesture,
+ const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+#if DEBUG_POINTER_UPDATES
+ LOGD("setSpots: spotGesture=%d", spotGesture);
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ const PointerCoords& c = spotCoords[spotIdToIndex[id]];
+ LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
+ c.getAxisValue(AMOTION_EVENT_AXIS_X),
+ c.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
}
+#endif
- if (wantVisibleAndHavePointerIcon) {
- status = mSurfaceControl->setPosition(
- mLocked.pointerX - mLocked.iconHotSpotX,
- mLocked.pointerY - mLocked.iconHotSpotY);
- if (status) {
- LOGE("Error %d moving pointer surface.", status);
- goto CloseTransaction;
- }
+ AutoMutex _l(mLock);
- status = mSurfaceControl->setAlpha(mLocked.fadeAlpha);
- if (status) {
- LOGE("Error %d setting pointer surface alpha.", status);
- goto CloseTransaction;
- }
+ mSpriteController->openTransaction();
- if (!mLocked.visible) {
- status = mSurfaceControl->setLayer(mPointerLayer);
- if (status) {
- LOGE("Error %d setting pointer surface layer.", status);
- goto CloseTransaction;
- }
+ // Add or move spots for fingers that are down.
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
- status = mSurfaceControl->show(mPointerLayer);
- if (status) {
- LOGE("Error %d showing pointer surface.", status);
- goto CloseTransaction;
- }
+ const PointerCoords& c = spotCoords[spotIdToIndex[id]];
+ const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
+ ? mResources.spotTouch : mResources.spotHover;
+ float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
+ float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
- mLocked.visible = true;
+ Spot* spot = getSpotLocked(id);
+ if (!spot) {
+ spot = createAndAddSpotLocked(id);
}
- } else {
- if (mLocked.visible) {
- status = mSurfaceControl->hide();
- if (status) {
- LOGE("Error %d hiding pointer surface.", status);
- goto CloseTransaction;
- }
- mLocked.visible = false;
+ spot->updateSprite(&icon, x, y);
+ }
+
+ // Remove spots for fingers that went up.
+ for (size_t i = 0; i < mLocked.spots.size(); i++) {
+ Spot* spot = mLocked.spots.itemAt(i);
+ if (spot->id != Spot::INVALID_ID
+ && !spotIdBits.hasBit(spot->id)) {
+ fadeOutAndReleaseSpotLocked(spot);
}
}
-CloseTransaction:
- status = mSurfaceComposerClient->closeTransaction();
- if (status) {
- LOGE("Error closing surface transaction to update pointer surface.");
+ mSpriteController->closeTransaction();
+}
+
+void PointerController::clearSpots() {
+#if DEBUG_POINTER_UPDATES
+ LOGD("clearSpots");
+#endif
+
+ AutoMutex _l(mLock);
+
+ fadeOutAndReleaseAllSpotsLocked();
+}
+
+void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
+ AutoMutex _l(mLock);
+
+ if (mLocked.inactivityTimeout != inactivityTimeout) {
+ mLocked.inactivityTimeout = inactivityTimeout;
+ resetInactivityTimeoutLocked();
}
}
@@ -301,7 +311,8 @@ void PointerController::setDisplaySize(int32_t width, int32_t height) {
mLocked.pointerY = 0;
}
- updateLocked();
+ fadeOutAndReleaseAllSpotsLocked();
+ updatePointerLocked();
}
}
@@ -339,7 +350,7 @@ void PointerController::setDisplayOrientation(int32_t orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = y;
- y = mLocked.displayWidth - x;
+ y = mLocked.displayWidth - temp;
break;
case DISPLAY_ORIENTATION_180:
x = mLocked.displayWidth - x;
@@ -358,173 +369,217 @@ void PointerController::setDisplayOrientation(int32_t orientation) {
mLocked.pointerY = y - 0.5f;
mLocked.displayOrientation = orientation;
- updateLocked();
+ updatePointerLocked();
}
}
-void PointerController::setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) {
+void PointerController::setPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
- if (mLocked.iconBitmap) {
- delete mLocked.iconBitmap;
- mLocked.iconBitmap = NULL;
- }
-
- if (bitmap) {
- mLocked.iconBitmap = new SkBitmap();
- bitmap->copyTo(mLocked.iconBitmap, SkBitmap::kARGB_8888_Config);
- }
+ mLocked.pointerIcon = icon.copy();
+ mLocked.pointerIconChanged = true;
- mLocked.iconHotSpotX = hotSpotX;
- mLocked.iconHotSpotY = hotSpotY;
- mLocked.drawn = false;
+ updatePointerLocked();
}
-bool PointerController::createSurfaceIfNeededLocked() {
- if (!mLocked.iconBitmap) {
- // If we don't have a pointer icon, then no point allocating a surface now.
- return false;
+void PointerController::handleMessage(const Message& message) {
+ switch (message.what) {
+ case MSG_ANIMATE:
+ doAnimate();
+ break;
+ case MSG_INACTIVITY_TIMEOUT:
+ doInactivityTimeout();
+ break;
}
+}
- if (mSurfaceComposerClient == NULL) {
- mSurfaceComposerClient = new SurfaceComposerClient();
+void PointerController::doAnimate() {
+ AutoMutex _l(mLock);
+
+ bool keepAnimating = false;
+ mLocked.animationPending = false;
+ nsecs_t frameDelay = systemTime(SYSTEM_TIME_MONOTONIC) - mLocked.animationTime;
+
+ // Animate pointer fade.
+ if (mLocked.pointerIsFading) {
+ mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
+ if (mLocked.pointerAlpha <= 0) {
+ mLocked.pointerAlpha = 0;
+ } else {
+ keepAnimating = true;
+ }
+ updatePointerLocked();
}
- if (mSurfaceControl == NULL) {
- mSurfaceControl = mSurfaceComposerClient->createSurface(getpid(),
- String8("Pointer Icon"), 0,
- mLocked.iconBitmap->width(), mLocked.iconBitmap->height(),
- PIXEL_FORMAT_RGBA_8888);
- if (mSurfaceControl == NULL) {
- LOGE("Error creating pointer surface.");
- return false;
+ // Animate spots that are fading out and being removed.
+ for (size_t i = 0; i < mLocked.spots.size(); i++) {
+ Spot* spot = mLocked.spots.itemAt(i);
+ if (spot->id == Spot::INVALID_ID) {
+ spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
+ if (spot->alpha <= 0) {
+ mLocked.spots.removeAt(i--);
+ releaseSpotLocked(spot);
+ } else {
+ spot->sprite->setAlpha(spot->alpha);
+ keepAnimating = true;
+ }
}
}
- return true;
+
+ if (keepAnimating) {
+ startAnimationLocked();
+ }
}
-bool PointerController::drawPointerIfNeededLocked() {
- if (!mLocked.drawn) {
- if (!mLocked.iconBitmap) {
- return false;
- }
+void PointerController::doInactivityTimeout() {
+ AutoMutex _l(mLock);
- if (!resizeSurfaceLocked(mLocked.iconBitmap->width(), mLocked.iconBitmap->height())) {
- return false;
- }
+ if (!mLocked.pointerIsFading) {
+ mLocked.pointerIsFading = true;
+ startAnimationLocked();
+ }
+}
- sp<Surface> surface = mSurfaceControl->getSurface();
+void PointerController::startAnimationLocked() {
+ if (!mLocked.animationPending) {
+ mLocked.animationPending = true;
+ mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE));
+ }
+}
- Surface::SurfaceInfo surfaceInfo;
- status_t status = surface->lock(&surfaceInfo);
- if (status) {
- LOGE("Error %d locking pointer surface before drawing.", status);
- return false;
- }
+void PointerController::resetInactivityTimeoutLocked() {
+ mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
- SkBitmap surfaceBitmap;
- ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format);
- surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config, surfaceInfo.w, surfaceInfo.h, bpr);
- surfaceBitmap.setPixels(surfaceInfo.bits);
+ nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
+ ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
+ mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
+}
- SkCanvas surfaceCanvas;
- surfaceCanvas.setBitmapDevice(surfaceBitmap);
+void PointerController::sendImmediateInactivityTimeoutLocked() {
+ mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
+ mLooper->sendMessage(mHandler, MSG_INACTIVITY_TIMEOUT);
+}
- SkPaint paint;
- paint.setXfermodeMode(SkXfermode::kSrc_Mode);
- surfaceCanvas.drawBitmap(*mLocked.iconBitmap, 0, 0, &paint);
+void PointerController::updatePointerLocked() {
+ mSpriteController->openTransaction();
- status = surface->unlockAndPost();
- if (status) {
- LOGE("Error %d unlocking pointer surface after drawing.", status);
- return false;
- }
+ mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
+ mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+
+ if (mLocked.pointerAlpha > 0) {
+ mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
+ mLocked.pointerSprite->setVisible(true);
+ } else {
+ mLocked.pointerSprite->setVisible(false);
}
- mLocked.drawn = true;
- return true;
+ if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
+ mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER
+ ? mLocked.pointerIcon : mResources.spotAnchor);
+ mLocked.pointerIconChanged = false;
+ mLocked.presentationChanged = false;
+ }
+
+ mSpriteController->closeTransaction();
}
-bool PointerController::resizeSurfaceLocked(int32_t width, int32_t height) {
- status_t status = mSurfaceComposerClient->openTransaction();
- if (status) {
- LOGE("Error opening surface transaction to resize pointer surface.");
- return false;
+PointerController::Spot* PointerController::getSpotLocked(uint32_t id) {
+ for (size_t i = 0; i < mLocked.spots.size(); i++) {
+ Spot* spot = mLocked.spots.itemAt(i);
+ if (spot->id == id) {
+ return spot;
+ }
}
+ return NULL;
+}
- status = mSurfaceControl->setSize(width, height);
- if (status) {
- LOGE("Error %d setting pointer surface size.", status);
- return false;
+PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) {
+ // Remove spots until we have fewer than MAX_SPOTS remaining.
+ while (mLocked.spots.size() >= MAX_SPOTS) {
+ Spot* spot = removeFirstFadingSpotLocked();
+ if (!spot) {
+ spot = mLocked.spots.itemAt(0);
+ mLocked.spots.removeAt(0);
+ }
+ releaseSpotLocked(spot);
}
- status = mSurfaceComposerClient->closeTransaction();
- if (status) {
- LOGE("Error closing surface transaction to resize pointer surface.");
- return false;
+ // Obtain a sprite from the recycled pool.
+ sp<Sprite> sprite;
+ if (! mLocked.recycledSprites.isEmpty()) {
+ sprite = mLocked.recycledSprites.top();
+ mLocked.recycledSprites.pop();
+ } else {
+ sprite = mSpriteController->createSprite();
}
- return true;
+ // Return the new spot.
+ Spot* spot = new Spot(id, sprite);
+ mLocked.spots.push(spot);
+ return spot;
}
-void PointerController::handleMessage(const Message& message) {
- switch (message.what) {
- case MSG_FADE_STEP: {
- AutoMutex _l(mLock);
- fadeStepLocked();
- break;
- }
+PointerController::Spot* PointerController::removeFirstFadingSpotLocked() {
+ for (size_t i = 0; i < mLocked.spots.size(); i++) {
+ Spot* spot = mLocked.spots.itemAt(i);
+ if (spot->id == Spot::INVALID_ID) {
+ mLocked.spots.removeAt(i);
+ return spot;
+ }
}
+ return NULL;
}
-bool PointerController::unfadeBeforeUpdateLocked() {
- sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
+void PointerController::releaseSpotLocked(Spot* spot) {
+ spot->sprite->clearIcon();
- if (isFadingLocked()) {
- mLocked.wantVisible = true;
- mLocked.fadeAlpha = 1;
- return true; // update required to effect the unfade
+ if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
+ mLocked.recycledSprites.push(spot->sprite);
}
- return false; // update not required
-}
-void PointerController::startFadeLocked() {
- if (!isFadingLocked()) {
- sendFadeStepMessageDelayedLocked(0);
- }
+ delete spot;
}
-void PointerController::startInactivityFadeDelayLocked() {
- if (!isFadingLocked()) {
- sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
+void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) {
+ if (spot->id != Spot::INVALID_ID) {
+ spot->id = Spot::INVALID_ID;
+ startAnimationLocked();
}
}
-void PointerController::fadeStepLocked() {
- if (mLocked.wantVisible) {
- mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME;
- if (mLocked.fadeAlpha < 0) {
- mLocked.fadeAlpha = 0;
- mLocked.wantVisible = false;
- } else {
- sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL);
- }
- updateLocked();
+void PointerController::fadeOutAndReleaseAllSpotsLocked() {
+ for (size_t i = 0; i < mLocked.spots.size(); i++) {
+ Spot* spot = mLocked.spots.itemAt(i);
+ fadeOutAndReleaseSpotLocked(spot);
}
}
-bool PointerController::isFadingLocked() {
- return !mLocked.wantVisible || mLocked.fadeAlpha != 1;
+void PointerController::loadResources() {
+ mPolicy->loadPointerResources(&mResources);
}
-nsecs_t PointerController::getInactivityFadeDelayTimeLocked() {
- return mLocked.inactivityFadeDelay == INACTIVITY_FADE_DELAY_SHORT
- ? INACTIVITY_FADE_DELAY_TIME_SHORT : INACTIVITY_FADE_DELAY_TIME_NORMAL;
-}
-void PointerController::sendFadeStepMessageDelayedLocked(nsecs_t delayTime) {
- mLooper->removeMessages(mHandler, MSG_FADE_STEP);
- mLooper->sendMessageDelayed(delayTime, mHandler, Message(MSG_FADE_STEP));
+// --- PointerController::Spot ---
+
+void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) {
+ sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
+ sprite->setAlpha(alpha);
+ sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
+ sprite->setPosition(x, y);
+
+ this->x = x;
+ this->y = y;
+
+ if (icon != lastIcon) {
+ lastIcon = icon;
+ if (icon) {
+ sprite->setIcon(*icon);
+ sprite->setVisible(true);
+ } else {
+ sprite->setVisible(false);
+ }
+ }
}
} // namespace android
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index e1dab5c..b9184ac 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -17,22 +17,23 @@
#ifndef _UI_POINTER_CONTROLLER_H
#define _UI_POINTER_CONTROLLER_H
+#include "SpriteController.h"
+
#include <ui/DisplayInfo.h>
#include <ui/Input.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
#include <utils/String8.h>
-#include <surfaceflinger/Surface.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
-#include <surfaceflinger/ISurfaceComposer.h>
-
#include <SkBitmap.h>
namespace android {
/**
- * Interface for tracking a single (mouse) pointer.
+ * Interface for tracking a mouse / touch pad pointer and touch pad spots.
+ *
+ * The spots are sprites on screen that visually represent the positions of
+ * fingers
*
* The pointer controller is responsible for providing synchronization and for tracking
* display orientation changes if needed.
@@ -66,8 +67,98 @@ public:
/* Fades the pointer out now. */
virtual void fade() = 0;
- /* Makes the pointer visible if it has faded out. */
+ /* Makes the pointer visible if it has faded out.
+ * The pointer never unfades itself automatically. This method must be called
+ * by the client whenever the pointer is moved or a button is pressed and it
+ * wants to ensure that the pointer becomes visible again. */
virtual void unfade() = 0;
+
+ enum Presentation {
+ // Show the mouse pointer.
+ PRESENTATION_POINTER,
+ // Show spots and a spot anchor in place of the mouse pointer.
+ PRESENTATION_SPOT,
+ };
+
+ /* Sets the mode of the pointer controller. */
+ virtual void setPresentation(Presentation presentation) = 0;
+
+ // Describes the current gesture.
+ enum SpotGesture {
+ // No gesture.
+ // Do not display any spots.
+ SPOT_GESTURE_NEUTRAL,
+ // Tap at current location.
+ // Briefly display one spot at the tapped location.
+ SPOT_GESTURE_TAP,
+ // Drag at current location.
+ // Display spot at pressed location.
+ SPOT_GESTURE_DRAG,
+ // Button pressed but no finger is down.
+ // Display spot at pressed location.
+ SPOT_GESTURE_BUTTON_CLICK,
+ // Button pressed and a finger is down.
+ // Display spot at pressed location.
+ SPOT_GESTURE_BUTTON_DRAG,
+ // One finger down and hovering.
+ // Display spot at the hovered location.
+ SPOT_GESTURE_HOVER,
+ // Two fingers down but not sure in which direction they are moving so we consider
+ // it a press at the pointer location.
+ // Display two spots near the pointer location.
+ SPOT_GESTURE_PRESS,
+ // Two fingers down and moving in same direction.
+ // Display two spots near the pointer location.
+ SPOT_GESTURE_SWIPE,
+ // Two or more fingers down and moving in arbitrary directions.
+ // Display two or more spots near the pointer location, one for each finger.
+ SPOT_GESTURE_FREEFORM,
+ };
+
+ /* Sets the spots for the current gesture.
+ * The spots are not subject to the inactivity timeout like the pointer
+ * itself it since they are expected to remain visible for so long as
+ * the fingers are on the touch pad.
+ *
+ * The values of the AMOTION_EVENT_AXIS_PRESSURE axis is significant.
+ * For spotCoords, pressure != 0 indicates that the spot's location is being
+ * pressed (not hovering).
+ */
+ virtual void setSpots(SpotGesture spotGesture,
+ const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+ BitSet32 spotIdBits) = 0;
+
+ /* Removes all spots. */
+ virtual void clearSpots() = 0;
+};
+
+
+/*
+ * Pointer resources.
+ */
+struct PointerResources {
+ SpriteIcon spotHover;
+ SpriteIcon spotTouch;
+ SpriteIcon spotAnchor;
+};
+
+
+/*
+ * Pointer controller policy interface.
+ *
+ * The pointer controller policy is used by the pointer controller to interact with
+ * the Window Manager and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI. This interface is also mocked in the unit tests.
+ */
+class PointerControllerPolicyInterface : public virtual RefBase {
+protected:
+ PointerControllerPolicyInterface() { }
+ virtual ~PointerControllerPolicyInterface() { }
+
+public:
+ virtual void loadPointerResources(PointerResources* outResources) = 0;
};
@@ -81,12 +172,13 @@ protected:
virtual ~PointerController();
public:
- enum InactivityFadeDelay {
- INACTIVITY_FADE_DELAY_NORMAL = 0,
- INACTIVITY_FADE_DELAY_SHORT = 1,
+ enum InactivityTimeout {
+ INACTIVITY_TIMEOUT_NORMAL = 0,
+ INACTIVITY_TIMEOUT_SHORT = 1,
};
- PointerController(const sp<Looper>& looper, int32_t pointerLayer);
+ PointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper, const sp<SpriteController>& spriteController);
virtual bool getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const;
@@ -98,61 +190,101 @@ public:
virtual void fade();
virtual void unfade();
+ virtual void setPresentation(Presentation presentation);
+ virtual void setSpots(SpotGesture spotGesture,
+ const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
+ virtual void clearSpots();
+
void setDisplaySize(int32_t width, int32_t height);
void setDisplayOrientation(int32_t orientation);
- void setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
- void setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay);
+ void setPointerIcon(const SpriteIcon& icon);
+ void setInactivityTimeout(InactivityTimeout inactivityTimeout);
private:
+ static const size_t MAX_RECYCLED_SPRITES = 12;
+ static const size_t MAX_SPOTS = 12;
+
enum {
- MSG_FADE_STEP = 0,
+ MSG_ANIMATE,
+ MSG_INACTIVITY_TIMEOUT,
+ };
+
+ struct Spot {
+ static const uint32_t INVALID_ID = 0xffffffff;
+
+ uint32_t id;
+ sp<Sprite> sprite;
+ float alpha;
+ float scale;
+ float x, y;
+
+ inline Spot(uint32_t id, const sp<Sprite>& sprite)
+ : id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
+ x(0.0f), y(0.0f), lastIcon(NULL) { }
+
+ void updateSprite(const SpriteIcon* icon, float x, float y);
+
+ private:
+ const SpriteIcon* lastIcon;
};
mutable Mutex mLock;
+ sp<PointerControllerPolicyInterface> mPolicy;
sp<Looper> mLooper;
- int32_t mPointerLayer;
- sp<SurfaceComposerClient> mSurfaceComposerClient;
- sp<SurfaceControl> mSurfaceControl;
+ sp<SpriteController> mSpriteController;
+ sp<WeakMessageHandler> mHandler;
+
+ PointerResources mResources;
struct Locked {
+ bool animationPending;
+ nsecs_t animationTime;
+
int32_t displayWidth;
int32_t displayHeight;
int32_t displayOrientation;
+ InactivityTimeout inactivityTimeout;
+
+ Presentation presentation;
+ bool presentationChanged;
+
+ bool pointerIsFading;
float pointerX;
float pointerY;
- uint32_t buttonState;
+ float pointerAlpha;
+ sp<Sprite> pointerSprite;
+ SpriteIcon pointerIcon;
+ bool pointerIconChanged;
- SkBitmap* iconBitmap;
- float iconHotSpotX;
- float iconHotSpotY;
-
- float fadeAlpha;
- InactivityFadeDelay inactivityFadeDelay;
+ uint32_t buttonState;
- bool wantVisible;
- bool visible;
- bool drawn;
+ Vector<Spot*> spots;
+ Vector<sp<Sprite> > recycledSprites;
} mLocked;
- sp<WeakMessageHandler> mHandler;
-
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
- void updateLocked();
- bool createSurfaceIfNeededLocked();
- bool drawPointerIfNeededLocked();
- bool resizeSurfaceLocked(int32_t width, int32_t height);
void handleMessage(const Message& message);
- bool unfadeBeforeUpdateLocked();
- void startFadeLocked();
- void startInactivityFadeDelayLocked();
- void fadeStepLocked();
- bool isFadingLocked();
- nsecs_t getInactivityFadeDelayTimeLocked();
- void sendFadeStepMessageDelayedLocked(nsecs_t delayTime);
+ void doAnimate();
+ void doInactivityTimeout();
+
+ void startAnimationLocked();
+
+ void resetInactivityTimeoutLocked();
+ void sendImmediateInactivityTimeoutLocked();
+ void updatePointerLocked();
+
+ Spot* getSpotLocked(uint32_t id);
+ Spot* createAndAddSpotLocked(uint32_t id);
+ Spot* removeFirstFadingSpotLocked();
+ void releaseSpotLocked(Spot* spot);
+ void fadeOutAndReleaseSpotLocked(Spot* spot);
+ void fadeOutAndReleaseAllSpotsLocked();
+
+ void loadResources();
};
} // namespace android
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
new file mode 100644
index 0000000..08cc75e
--- /dev/null
+++ b/services/input/SpriteController.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Sprites"
+
+//#define LOG_NDEBUG 0
+
+#include "SpriteController.h"
+
+#include <cutils/log.h>
+#include <utils/String8.h>
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+#include <SkXfermode.h>
+
+namespace android {
+
+// --- SpriteController ---
+
+SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) :
+ mLooper(looper), mOverlayLayer(overlayLayer) {
+ mHandler = new WeakMessageHandler(this);
+
+ mLocked.transactionNestingCount = 0;
+ mLocked.deferredSpriteUpdate = false;
+}
+
+SpriteController::~SpriteController() {
+ mLooper->removeMessages(mHandler);
+
+ if (mSurfaceComposerClient != NULL) {
+ mSurfaceComposerClient->dispose();
+ mSurfaceComposerClient.clear();
+ }
+}
+
+sp<Sprite> SpriteController::createSprite() {
+ return new SpriteImpl(this);
+}
+
+void SpriteController::openTransaction() {
+ AutoMutex _l(mLock);
+
+ mLocked.transactionNestingCount += 1;
+}
+
+void SpriteController::closeTransaction() {
+ AutoMutex _l(mLock);
+
+ LOG_ALWAYS_FATAL_IF(mLocked.transactionNestingCount == 0,
+ "Sprite closeTransaction() called but there is no open sprite transaction");
+
+ mLocked.transactionNestingCount -= 1;
+ if (mLocked.transactionNestingCount == 0 && mLocked.deferredSpriteUpdate) {
+ mLocked.deferredSpriteUpdate = false;
+ mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
+ }
+}
+
+void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
+ bool wasEmpty = mLocked.invalidatedSprites.isEmpty();
+ mLocked.invalidatedSprites.push(sprite);
+ if (wasEmpty) {
+ if (mLocked.transactionNestingCount != 0) {
+ mLocked.deferredSpriteUpdate = true;
+ } else {
+ mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
+ }
+ }
+}
+
+void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) {
+ bool wasEmpty = mLocked.disposedSurfaces.isEmpty();
+ mLocked.disposedSurfaces.push(surfaceControl);
+ if (wasEmpty) {
+ mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
+ }
+}
+
+void SpriteController::handleMessage(const Message& message) {
+ switch (message.what) {
+ case MSG_UPDATE_SPRITES:
+ doUpdateSprites();
+ break;
+ case MSG_DISPOSE_SURFACES:
+ doDisposeSurfaces();
+ break;
+ }
+}
+
+void SpriteController::doUpdateSprites() {
+ // Collect information about sprite updates.
+ // Each sprite update record includes a reference to its associated sprite so we can
+ // be certain the sprites will not be deleted while this function runs. Sprites
+ // may invalidate themselves again during this time but we will handle those changes
+ // in the next iteration.
+ Vector<SpriteUpdate> updates;
+ size_t numSprites;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ numSprites = mLocked.invalidatedSprites.size();
+ for (size_t i = 0; i < numSprites; i++) {
+ const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i);
+
+ updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
+ sprite->resetDirtyLocked();
+ }
+ mLocked.invalidatedSprites.clear();
+ } // release lock
+
+ // Create missing surfaces.
+ bool surfaceChanged = false;
+ for (size_t i = 0; i < numSprites; i++) {
+ SpriteUpdate& update = updates.editItemAt(i);
+
+ if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
+ update.state.surfaceWidth = update.state.icon.bitmap.width();
+ update.state.surfaceHeight = update.state.icon.bitmap.height();
+ update.state.surfaceDrawn = false;
+ update.state.surfaceVisible = false;
+ update.state.surfaceControl = obtainSurface(
+ update.state.surfaceWidth, update.state.surfaceHeight);
+ if (update.state.surfaceControl != NULL) {
+ update.surfaceChanged = surfaceChanged = true;
+ }
+ }
+ }
+
+ // Resize sprites if needed, inside a global transaction.
+ bool haveGlobalTransaction = false;
+ for (size_t i = 0; i < numSprites; i++) {
+ SpriteUpdate& update = updates.editItemAt(i);
+
+ if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+ int32_t desiredWidth = update.state.icon.bitmap.width();
+ int32_t desiredHeight = update.state.icon.bitmap.height();
+ if (update.state.surfaceWidth < desiredWidth
+ || update.state.surfaceHeight < desiredHeight) {
+ if (!haveGlobalTransaction) {
+ SurfaceComposerClient::openGlobalTransaction();
+ haveGlobalTransaction = true;
+ }
+
+ status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight);
+ if (status) {
+ LOGE("Error %d resizing sprite surface from %dx%d to %dx%d",
+ status, update.state.surfaceWidth, update.state.surfaceHeight,
+ desiredWidth, desiredHeight);
+ } else {
+ update.state.surfaceWidth = desiredWidth;
+ update.state.surfaceHeight = desiredHeight;
+ update.state.surfaceDrawn = false;
+ update.surfaceChanged = surfaceChanged = true;
+
+ if (update.state.surfaceVisible) {
+ status = update.state.surfaceControl->hide();
+ if (status) {
+ LOGE("Error %d hiding sprite surface after resize.", status);
+ } else {
+ update.state.surfaceVisible = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (haveGlobalTransaction) {
+ SurfaceComposerClient::closeGlobalTransaction();
+ }
+
+ // Redraw sprites if needed.
+ for (size_t i = 0; i < numSprites; i++) {
+ SpriteUpdate& update = updates.editItemAt(i);
+
+ if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) {
+ update.state.surfaceDrawn = false;
+ update.surfaceChanged = surfaceChanged = true;
+ }
+
+ if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
+ && update.state.wantSurfaceVisible()) {
+ sp<Surface> surface = update.state.surfaceControl->getSurface();
+ Surface::SurfaceInfo surfaceInfo;
+ status_t status = surface->lock(&surfaceInfo);
+ if (status) {
+ LOGE("Error %d locking sprite surface before drawing.", status);
+ } else {
+ SkBitmap surfaceBitmap;
+ ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format);
+ surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ surfaceInfo.w, surfaceInfo.h, bpr);
+ surfaceBitmap.setPixels(surfaceInfo.bits);
+
+ SkCanvas surfaceCanvas;
+ surfaceCanvas.setBitmapDevice(surfaceBitmap);
+
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
+
+ if (surfaceInfo.w > uint32_t(update.state.icon.bitmap.width())) {
+ paint.setColor(0); // transparent fill color
+ surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0,
+ surfaceInfo.w, update.state.icon.bitmap.height(), paint);
+ }
+ if (surfaceInfo.h > uint32_t(update.state.icon.bitmap.height())) {
+ paint.setColor(0); // transparent fill color
+ surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(),
+ surfaceInfo.w, surfaceInfo.h, paint);
+ }
+
+ status = surface->unlockAndPost();
+ if (status) {
+ LOGE("Error %d unlocking and posting sprite surface after drawing.", status);
+ } else {
+ update.state.surfaceDrawn = true;
+ update.surfaceChanged = surfaceChanged = true;
+ }
+ }
+ }
+ }
+
+ // Set sprite surface properties and make them visible.
+ bool haveTransaction = false;
+ for (size_t i = 0; i < numSprites; i++) {
+ SpriteUpdate& update = updates.editItemAt(i);
+
+ bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible()
+ && update.state.surfaceDrawn;
+ bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible;
+ bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible;
+ if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
+ || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
+ | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+ status_t status;
+ if (!haveTransaction) {
+ status = mSurfaceComposerClient->openTransaction();
+ if (status) {
+ LOGE("Error %d opening transation to update sprite surface.", status);
+ break;
+ }
+ haveTransaction = true;
+ }
+
+ if (wantSurfaceVisibleAndDrawn
+ && (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) {
+ status = update.state.surfaceControl->setAlpha(update.state.alpha);
+ if (status) {
+ LOGE("Error %d setting sprite surface alpha.", status);
+ }
+ }
+
+ if (wantSurfaceVisibleAndDrawn
+ && (becomingVisible || (update.state.dirty & (DIRTY_POSITION
+ | DIRTY_HOTSPOT)))) {
+ status = update.state.surfaceControl->setPosition(
+ update.state.positionX - update.state.icon.hotSpotX,
+ update.state.positionY - update.state.icon.hotSpotY);
+ if (status) {
+ LOGE("Error %d setting sprite surface position.", status);
+ }
+ }
+
+ if (wantSurfaceVisibleAndDrawn
+ && (becomingVisible
+ || (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) {
+ status = update.state.surfaceControl->setMatrix(
+ update.state.transformationMatrix.dsdx,
+ update.state.transformationMatrix.dtdx,
+ update.state.transformationMatrix.dsdy,
+ update.state.transformationMatrix.dtdy);
+ if (status) {
+ LOGE("Error %d setting sprite surface transformation matrix.", status);
+ }
+ }
+
+ int32_t surfaceLayer = mOverlayLayer + update.state.layer;
+ if (wantSurfaceVisibleAndDrawn
+ && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
+ status = update.state.surfaceControl->setLayer(surfaceLayer);
+ if (status) {
+ LOGE("Error %d setting sprite surface layer.", status);
+ }
+ }
+
+ if (becomingVisible) {
+ status = update.state.surfaceControl->show(surfaceLayer);
+ if (status) {
+ LOGE("Error %d showing sprite surface.", status);
+ } else {
+ update.state.surfaceVisible = true;
+ update.surfaceChanged = surfaceChanged = true;
+ }
+ } else if (becomingHidden) {
+ status = update.state.surfaceControl->hide();
+ if (status) {
+ LOGE("Error %d hiding sprite surface.", status);
+ } else {
+ update.state.surfaceVisible = false;
+ update.surfaceChanged = surfaceChanged = true;
+ }
+ }
+ }
+ }
+
+ if (haveTransaction) {
+ status_t status = mSurfaceComposerClient->closeTransaction();
+ if (status) {
+ LOGE("Error %d closing transaction to update sprite surface.", status);
+ }
+ }
+
+ // If any surfaces were changed, write back the new surface properties to the sprites.
+ if (surfaceChanged) { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = 0; i < numSprites; i++) {
+ const SpriteUpdate& update = updates.itemAt(i);
+
+ if (update.surfaceChanged) {
+ update.sprite->setSurfaceLocked(update.state.surfaceControl,
+ update.state.surfaceWidth, update.state.surfaceHeight,
+ update.state.surfaceDrawn, update.state.surfaceVisible);
+ }
+ }
+ } // release lock
+
+ // Clear the sprite update vector outside the lock. It is very important that
+ // we do not clear sprite references inside the lock since we could be releasing
+ // the last remaining reference to the sprite here which would result in the
+ // sprite being deleted and the lock being reacquired by the sprite destructor
+ // while already held.
+ updates.clear();
+}
+
+void SpriteController::doDisposeSurfaces() {
+ // Collect disposed surfaces.
+ Vector<sp<SurfaceControl> > disposedSurfaces;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ disposedSurfaces = mLocked.disposedSurfaces;
+ mLocked.disposedSurfaces.clear();
+ } // release lock
+
+ // Release the last reference to each surface outside of the lock.
+ // We don't want the surfaces to be deleted while we are holding our lock.
+ disposedSurfaces.clear();
+}
+
+void SpriteController::ensureSurfaceComposerClient() {
+ if (mSurfaceComposerClient == NULL) {
+ mSurfaceComposerClient = new SurfaceComposerClient();
+ }
+}
+
+sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) {
+ ensureSurfaceComposerClient();
+
+ sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
+ String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888);
+ if (surfaceControl == NULL || !surfaceControl->isValid()
+ || !surfaceControl->getSurface()->isValid()) {
+ LOGE("Error creating sprite surface.");
+ return NULL;
+ }
+ return surfaceControl;
+}
+
+
+// --- SpriteController::SpriteImpl ---
+
+SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
+ mController(controller) {
+}
+
+SpriteController::SpriteImpl::~SpriteImpl() {
+ AutoMutex _m(mController->mLock);
+
+ // Let the controller take care of deleting the last reference to sprite
+ // surfaces so that we do not block the caller on an IPC here.
+ if (mLocked.state.surfaceControl != NULL) {
+ mController->disposeSurfaceLocked(mLocked.state.surfaceControl);
+ mLocked.state.surfaceControl.clear();
+ }
+}
+
+void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
+ AutoMutex _l(mController->mLock);
+
+ uint32_t dirty;
+ if (icon.isValid()) {
+ icon.bitmap.copyTo(&mLocked.state.icon.bitmap, SkBitmap::kARGB_8888_Config);
+
+ if (!mLocked.state.icon.isValid()
+ || mLocked.state.icon.hotSpotX != icon.hotSpotX
+ || mLocked.state.icon.hotSpotY != icon.hotSpotY) {
+ mLocked.state.icon.hotSpotX = icon.hotSpotX;
+ mLocked.state.icon.hotSpotY = icon.hotSpotY;
+ dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+ } else {
+ dirty = DIRTY_BITMAP;
+ }
+ } else if (mLocked.state.icon.isValid()) {
+ mLocked.state.icon.bitmap.reset();
+ dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+ } else {
+ return; // setting to invalid icon and already invalid so nothing to do
+ }
+
+ invalidateLocked(dirty);
+}
+
+void SpriteController::SpriteImpl::setVisible(bool visible) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.visible != visible) {
+ mLocked.state.visible = visible;
+ invalidateLocked(DIRTY_VISIBILITY);
+ }
+}
+
+void SpriteController::SpriteImpl::setPosition(float x, float y) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.positionX != x || mLocked.state.positionY != y) {
+ mLocked.state.positionX = x;
+ mLocked.state.positionY = y;
+ invalidateLocked(DIRTY_POSITION);
+ }
+}
+
+void SpriteController::SpriteImpl::setLayer(int32_t layer) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.layer != layer) {
+ mLocked.state.layer = layer;
+ invalidateLocked(DIRTY_LAYER);
+ }
+}
+
+void SpriteController::SpriteImpl::setAlpha(float alpha) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.alpha != alpha) {
+ mLocked.state.alpha = alpha;
+ invalidateLocked(DIRTY_ALPHA);
+ }
+}
+
+void SpriteController::SpriteImpl::setTransformationMatrix(
+ const SpriteTransformationMatrix& matrix) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.transformationMatrix != matrix) {
+ mLocked.state.transformationMatrix = matrix;
+ invalidateLocked(DIRTY_TRANSFORMATION_MATRIX);
+ }
+}
+
+void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
+ bool wasDirty = mLocked.state.dirty;
+ mLocked.state.dirty |= dirty;
+
+ if (!wasDirty) {
+ mController->invalidateSpriteLocked(this);
+ }
+}
+
+} // namespace android
diff --git a/services/input/SpriteController.h b/services/input/SpriteController.h
new file mode 100644
index 0000000..50ae8a5
--- /dev/null
+++ b/services/input/SpriteController.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_SPRITES_H
+#define _UI_SPRITES_H
+
+#include <utils/RefBase.h>
+#include <utils/Looper.h>
+
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <SkBitmap.h>
+
+namespace android {
+
+/*
+ * Transformation matrix for a sprite.
+ */
+struct SpriteTransformationMatrix {
+ inline SpriteTransformationMatrix() : dsdx(1.0f), dtdx(0.0f), dsdy(0.0f), dtdy(1.0f) { }
+ inline SpriteTransformationMatrix(float dsdx, float dtdx, float dsdy, float dtdy) :
+ dsdx(dsdx), dtdx(dtdx), dsdy(dsdy), dtdy(dtdy) { }
+
+ float dsdx;
+ float dtdx;
+ float dsdy;
+ float dtdy;
+
+ inline bool operator== (const SpriteTransformationMatrix& other) {
+ return dsdx == other.dsdx
+ && dtdx == other.dtdx
+ && dsdy == other.dsdy
+ && dtdy == other.dtdy;
+ }
+
+ inline bool operator!= (const SpriteTransformationMatrix& other) {
+ return !(*this == other);
+ }
+};
+
+/*
+ * Icon that a sprite displays, including its hotspot.
+ */
+struct SpriteIcon {
+ inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
+ inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
+ bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
+
+ SkBitmap bitmap;
+ float hotSpotX;
+ float hotSpotY;
+
+ inline SpriteIcon copy() const {
+ SkBitmap bitmapCopy;
+ bitmap.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config);
+ return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
+ }
+
+ inline void reset() {
+ bitmap.reset();
+ hotSpotX = 0;
+ hotSpotY = 0;
+ }
+
+ inline bool isValid() const {
+ return !bitmap.isNull() && !bitmap.empty();
+ }
+};
+
+/*
+ * A sprite is a simple graphical object that is displayed on-screen above other layers.
+ * The basic sprite class is an interface.
+ * The implementation is provided by the sprite controller.
+ */
+class Sprite : public RefBase {
+protected:
+ Sprite() { }
+ virtual ~Sprite() { }
+
+public:
+ enum {
+ // The base layer for pointer sprites.
+ BASE_LAYER_POINTER = 0, // reserve space for 1 pointer
+
+ // The base layer for spot sprites.
+ BASE_LAYER_SPOT = 1, // reserve space for MAX_POINTER_ID spots
+ };
+
+ /* Sets the bitmap that is drawn by the sprite.
+ * The sprite retains a copy of the bitmap for subsequent rendering. */
+ virtual void setIcon(const SpriteIcon& icon) = 0;
+
+ inline void clearIcon() {
+ setIcon(SpriteIcon());
+ }
+
+ /* Sets whether the sprite is visible. */
+ virtual void setVisible(bool visible) = 0;
+
+ /* Sets the sprite position on screen, relative to the sprite's hot spot. */
+ virtual void setPosition(float x, float y) = 0;
+
+ /* Sets the layer of the sprite, relative to the system sprite overlay layer.
+ * Layer 0 is the overlay layer, > 0 appear above this layer. */
+ virtual void setLayer(int32_t layer) = 0;
+
+ /* Sets the sprite alpha blend ratio between 0.0 and 1.0. */
+ virtual void setAlpha(float alpha) = 0;
+
+ /* Sets the sprite transformation matrix. */
+ virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+};
+
+/*
+ * Displays sprites on the screen.
+ *
+ * This interface is used by PointerController and SpotController to draw pointers or
+ * spot representations of fingers. It is not intended for general purpose use
+ * by other components.
+ *
+ * All sprite position updates and rendering is performed asynchronously.
+ *
+ * Clients are responsible for animating sprites by periodically updating their properties.
+ */
+class SpriteController : public MessageHandler {
+protected:
+ virtual ~SpriteController();
+
+public:
+ SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
+
+ /* Creates a new sprite, initially invisible. */
+ sp<Sprite> createSprite();
+
+ /* Opens or closes a transaction to perform a batch of sprite updates as part of
+ * a single operation such as setPosition and setAlpha. It is not necessary to
+ * open a transaction when updating a single property.
+ * Calls to openTransaction() nest and must be matched by an equal number
+ * of calls to closeTransaction(). */
+ void openTransaction();
+ void closeTransaction();
+
+private:
+ enum {
+ MSG_UPDATE_SPRITES,
+ MSG_DISPOSE_SURFACES,
+ };
+
+ enum {
+ DIRTY_BITMAP = 1 << 0,
+ DIRTY_ALPHA = 1 << 1,
+ DIRTY_POSITION = 1 << 2,
+ DIRTY_TRANSFORMATION_MATRIX = 1 << 3,
+ DIRTY_LAYER = 1 << 4,
+ DIRTY_VISIBILITY = 1 << 5,
+ DIRTY_HOTSPOT = 1 << 6,
+ };
+
+ /* Describes the state of a sprite.
+ * This structure is designed so that it can be copied during updates so that
+ * surfaces can be resized and redrawn without blocking the client by holding a lock
+ * on the sprites for a long time.
+ * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
+ struct SpriteState {
+ inline SpriteState() :
+ dirty(0), visible(false),
+ positionX(0), positionY(0), layer(0), alpha(1.0f),
+ surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
+ }
+
+ uint32_t dirty;
+
+ SpriteIcon icon;
+ bool visible;
+ float positionX;
+ float positionY;
+ int32_t layer;
+ float alpha;
+ SpriteTransformationMatrix transformationMatrix;
+
+ sp<SurfaceControl> surfaceControl;
+ int32_t surfaceWidth;
+ int32_t surfaceHeight;
+ bool surfaceDrawn;
+ bool surfaceVisible;
+
+ inline bool wantSurfaceVisible() const {
+ return visible && alpha > 0.0f && icon.isValid();
+ }
+ };
+
+ /* Client interface for a sprite.
+ * Requests acquire a lock on the controller, update local state and request the
+ * controller to invalidate the sprite.
+ * The real heavy lifting of creating, resizing and redrawing surfaces happens
+ * asynchronously with no locks held except in short critical section to copy
+ * the sprite state before the work and update the sprite surface control afterwards.
+ */
+ class SpriteImpl : public Sprite {
+ protected:
+ virtual ~SpriteImpl();
+
+ public:
+ SpriteImpl(const sp<SpriteController> controller);
+
+ virtual void setIcon(const SpriteIcon& icon);
+ virtual void setVisible(bool visible);
+ virtual void setPosition(float x, float y);
+ virtual void setLayer(int32_t layer);
+ virtual void setAlpha(float alpha);
+ virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+
+ inline const SpriteState& getStateLocked() const {
+ return mLocked.state;
+ }
+
+ inline void resetDirtyLocked() {
+ mLocked.state.dirty = 0;
+ }
+
+ inline void setSurfaceLocked(const sp<SurfaceControl>& surfaceControl,
+ int32_t width, int32_t height, bool drawn, bool visible) {
+ mLocked.state.surfaceControl = surfaceControl;
+ mLocked.state.surfaceWidth = width;
+ mLocked.state.surfaceHeight = height;
+ mLocked.state.surfaceDrawn = drawn;
+ mLocked.state.surfaceVisible = visible;
+ }
+
+ private:
+ sp<SpriteController> mController;
+
+ struct Locked {
+ SpriteState state;
+ } mLocked; // guarded by mController->mLock
+
+ void invalidateLocked(uint32_t dirty);
+ };
+
+ /* Stores temporary information collected during the sprite update cycle. */
+ struct SpriteUpdate {
+ inline SpriteUpdate() : surfaceChanged(false) { }
+ inline SpriteUpdate(const sp<SpriteImpl> sprite, const SpriteState& state) :
+ sprite(sprite), state(state), surfaceChanged(false) {
+ }
+
+ sp<SpriteImpl> sprite;
+ SpriteState state;
+ bool surfaceChanged;
+ };
+
+ mutable Mutex mLock;
+
+ sp<Looper> mLooper;
+ const int32_t mOverlayLayer;
+ sp<WeakMessageHandler> mHandler;
+
+ sp<SurfaceComposerClient> mSurfaceComposerClient;
+
+ struct Locked {
+ Vector<sp<SpriteImpl> > invalidatedSprites;
+ Vector<sp<SurfaceControl> > disposedSurfaces;
+ uint32_t transactionNestingCount;
+ bool deferredSpriteUpdate;
+ } mLocked; // guarded by mLock
+
+ void invalidateSpriteLocked(const sp<SpriteImpl>& sprite);
+ void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl);
+
+ void handleMessage(const Message& message);
+ void doUpdateSprites();
+ void doDisposeSurfaces();
+
+ void ensureSurfaceComposerClient();
+ sp<SurfaceControl> obtainSurface(int32_t width, int32_t height);
+};
+
+} // namespace android
+
+#endif // _UI_SPRITES_H
diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk
index 799eb76..cabbccb 100644
--- a/services/input/tests/Android.mk
+++ b/services/input/tests/Android.mk
@@ -15,7 +15,6 @@ shared_libraries := \
libhardware \
libhardware_legacy \
libui \
- libsurfaceflinger_client \
libskia \
libstlport \
libinput
diff --git a/services/input/tests/InputDispatcher_test.cpp b/services/input/tests/InputDispatcher_test.cpp
index 2f846c4..3650da0 100644
--- a/services/input/tests/InputDispatcher_test.cpp
+++ b/services/input/tests/InputDispatcher_test.cpp
@@ -67,6 +67,10 @@ private:
return 60;
}
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+ return true;
+ }
+
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
}
@@ -124,7 +128,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
/*action*/ -1, 0,
AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
@@ -132,7 +136,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
AKEY_EVENT_ACTION_MULTIPLE, 0,
AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject key events with ACTION_MULTIPLE.";
}
@@ -150,7 +154,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
@@ -160,7 +164,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer down index too large.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -169,7 +173,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
@@ -179,7 +183,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer up index too large.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -188,7 +192,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
@@ -197,7 +201,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with 0 pointers.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -205,7 +209,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with more than MAX_POINTERS pointers.";
// Rejects motion events with invalid pointer ids.
@@ -215,7 +219,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer ids less than 0.";
pointerIds[0] = MAX_POINTER_ID + 1;
@@ -224,7 +228,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
// Rejects motion events with duplicate pointer ids.
@@ -235,7 +239,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with duplicate pointer ids.";
}
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 32982c4..54bb9d7 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -97,6 +97,16 @@ private:
virtual void unfade() {
}
+
+ virtual void setPresentation(Presentation presentation) {
+ }
+
+ virtual void setSpots(SpotGesture spotGesture,
+ const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+ }
+
+ virtual void clearSpots() {
+ }
};
@@ -369,7 +379,8 @@ private:
}
virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ uint32_t policyFlags) {
ADD_FAILURE() << "Should never be called by input reader.";
return INPUT_EVENT_INJECTION_FAILED;
}
@@ -386,6 +397,10 @@ private:
ADD_FAILURE() << "Should never be called by input reader.";
}
+ virtual void setInputFilterEnabled(bool enabled) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ }
+
virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
const sp<InputChannel>& toChannel) {
ADD_FAILURE() << "Should never be called by input reader.";
@@ -622,14 +637,14 @@ private:
mExcludedDevices.add(String8(deviceName));
}
- virtual bool getEvent(RawEvent* outEvent) {
+ virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
if (mEvents.empty()) {
- return false;
+ return 0;
}
- *outEvent = *mEvents.begin();
+ *buffer = *mEvents.begin();
mEvents.erase(mEvents.begin());
- return true;
+ return 1;
}
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
@@ -780,6 +795,9 @@ private:
virtual void fadePointer() {
}
+
+ virtual void requestTimeoutAtTime(nsecs_t when) {
+ }
};
@@ -1442,7 +1460,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe
// Event handling.
RawEvent event;
- mDevice->process(&event);
+ mDevice->process(&event, 1);
ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled());
ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled());
@@ -2460,8 +2478,18 @@ void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
}
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecified_ReturnsTouchPad) {
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
+}
+
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) {
SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+ mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X);
+ mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y);
prepareAxes(POSITION);
addMapperAndConfigure(mapper);
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index a4a95a0..c03b994 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -541,7 +541,9 @@ class AppWidgetService extends IAppWidgetService.Stub
IRemoteViewsFactory.Stub.asInterface(service);
try {
cb.onDestroy(intent);
- } catch (Exception e) {
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
e.printStackTrace();
}
mContext.unbindService(this);
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index ea38fbb..a334dbb 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -23,10 +23,14 @@ import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IBackupAgent;
import android.app.PendingIntent;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackup;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
+import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -60,6 +64,9 @@ import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.StringBuilderPrinter;
+
+import libcore.io.Libcore;
import com.android.internal.backup.BackupConstants;
import com.android.internal.backup.IBackupTransport;
@@ -81,10 +88,15 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
class BackupManagerService extends IBackupManager.Stub {
private static final String TAG = "BackupManagerService";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
+
+ // Name and current contents version of the full-backup manifest file
+ static final String BACKUP_MANIFEST_FILENAME = "_manifest";
+ static final int BACKUP_MANIFEST_VERSION = 1;
// How often we perform a backup pass. Privileged external callers can
// trigger an immediate pass.
@@ -108,14 +120,20 @@ class BackupManagerService extends IBackupManager.Stub {
private static final int MSG_RUN_GET_RESTORE_SETS = 6;
private static final int MSG_TIMEOUT = 7;
private static final int MSG_RESTORE_TIMEOUT = 8;
+ private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
+ private static final int MSG_RUN_FULL_RESTORE = 10;
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
// Timeout intervals for agent backup & restore operations
static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
+ static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
+ // User confirmation timeout for a full backup/restore operation
+ static final long TIMEOUT_FULL_CONFIRMATION = 30 * 1000;
+
private Context mContext;
private PackageManager mPackageManager;
IPackageManager mPackageManagerBinder;
@@ -138,15 +156,13 @@ class BackupManagerService extends IBackupManager.Stub {
// set of backup services that have pending changes
class BackupRequest {
public ApplicationInfo appInfo;
- public boolean fullBackup;
- BackupRequest(ApplicationInfo app, boolean isFull) {
+ BackupRequest(ApplicationInfo app) {
appInfo = app;
- fullBackup = isFull;
}
public String toString() {
- return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
+ return "BackupRequest{app=" + appInfo + "}";
}
}
// Backups that we haven't started yet. Keys are package names.
@@ -232,6 +248,38 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ class FullParams {
+ public ParcelFileDescriptor fd;
+ public final AtomicBoolean latch;
+ public IFullBackupRestoreObserver observer;
+
+ FullParams() {
+ latch = new AtomicBoolean(false);
+ }
+ }
+
+ class FullBackupParams extends FullParams {
+ public boolean includeApks;
+ public boolean includeShared;
+ public boolean allApps;
+ public String[] packages;
+
+ FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
+ boolean doAllApps, String[] pkgList) {
+ fd = output;
+ includeApks = saveApks;
+ includeShared = saveShared;
+ allApps = doAllApps;
+ packages = pkgList;
+ }
+ }
+
+ class FullRestoreParams extends FullParams {
+ FullRestoreParams(ParcelFileDescriptor input) {
+ fd = input;
+ }
+ }
+
// Bookkeeping of in-flight operations for timeout etc. purposes. The operation
// token is the index of the entry in the pending-operations list.
static final int OP_PENDING = 0;
@@ -242,6 +290,8 @@ class BackupManagerService extends IBackupManager.Stub {
final Object mCurrentOpLock = new Object();
final Random mTokenGenerator = new Random();
+ final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
+
// Where we keep our journal files and other bookkeeping
File mBaseStateDir;
File mDataDir;
@@ -264,6 +314,17 @@ class BackupManagerService extends IBackupManager.Stub {
static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
HashSet<String> mPendingInits = new HashSet<String>(); // transport names
+ // Utility: build a new random integer token
+ int generateToken() {
+ int token;
+ do {
+ synchronized (mTokenGenerator) {
+ token = mTokenGenerator.nextInt();
+ }
+ } while (token < 0);
+ return token;
+ }
+
// ----- Asynchronous backup/restore handler thread -----
private class BackupHandler extends Handler {
@@ -321,7 +382,13 @@ class BackupManagerService extends IBackupManager.Stub {
}
case MSG_RUN_FULL_BACKUP:
+ {
+ FullBackupParams params = (FullBackupParams)msg.obj;
+ (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
+ params.includeShared, params.allApps, params.packages,
+ params.latch)).run();
break;
+ }
case MSG_RUN_RESTORE:
{
@@ -416,6 +483,34 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
}
+
+ case MSG_FULL_CONFIRMATION_TIMEOUT:
+ {
+ synchronized (mFullConfirmations) {
+ FullParams params = mFullConfirmations.get(msg.arg1);
+ if (params != null) {
+ Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
+
+ // Release the waiter; timeout == completion
+ signalFullBackupRestoreCompletion(params);
+
+ // Remove the token from the set
+ mFullConfirmations.delete(msg.arg1);
+
+ // Report a timeout to the observer, if any
+ if (params.observer != null) {
+ try {
+ params.observer.onTimeout();
+ } catch (RemoteException e) {
+ /* don't care if the app has gone away */
+ }
+ }
+ } else {
+ Slog.d(TAG, "couldn't find params for token " + msg.arg1);
+ }
+ }
+ break;
+ }
}
}
}
@@ -760,15 +855,15 @@ class BackupManagerService extends IBackupManager.Stub {
sf.delete();
}
}
+ }
- // Enqueue a new backup of every participant
- int N = mBackupParticipants.size();
- for (int i=0; i<N; i++) {
- int uid = mBackupParticipants.keyAt(i);
- HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
- for (ApplicationInfo app: participants) {
- dataChangedImpl(app.packageName);
- }
+ // Enqueue a new backup of every participant
+ int N = mBackupParticipants.size();
+ for (int i=0; i<N; i++) {
+ int uid = mBackupParticipants.keyAt(i);
+ HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
+ for (ApplicationInfo app: participants) {
+ dataChangedImpl(app.packageName);
}
}
}
@@ -1253,9 +1348,11 @@ class BackupManagerService extends IBackupManager.Stub {
void prepareOperationTimeout(int token, long interval) {
if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+ " interval=" + interval);
- mCurrentOperations.put(token, OP_PENDING);
- Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
- mBackupHandler.sendMessageDelayed(msg, interval);
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.put(token, OP_PENDING);
+ Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
+ mBackupHandler.sendMessageDelayed(msg, interval);
+ }
}
// ----- Back up a set of applications via a worker thread -----
@@ -1313,7 +1410,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (status == BackupConstants.TRANSPORT_OK) {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
mPackageManager, allAgentPackages());
- BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+ BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
status = processOneBackup(pmRequest,
IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
@@ -1407,12 +1504,10 @@ class BackupManagerService extends IBackupManager.Stub {
}
IBackupAgent agent = null;
- int mode = (request.fullBackup)
- ? IApplicationThread.BACKUP_MODE_FULL
- : IApplicationThread.BACKUP_MODE_INCREMENTAL;
try {
mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
- agent = bindToAgentSynchronous(request.appInfo, mode);
+ agent = bindToAgentSynchronous(request.appInfo,
+ IApplicationThread.BACKUP_MODE_INCREMENTAL);
if (agent != null) {
int result = processOneBackup(request, agent, transport);
if (result != BackupConstants.TRANSPORT_OK) return result;
@@ -1446,7 +1541,7 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor newState = null;
PackageInfo packInfo;
- int token = mTokenGenerator.nextInt();
+ final int token = generateToken();
try {
// Look up the package info & signatures. This is first so that if it
// throws an exception, there's no file setup yet that would need to
@@ -1461,12 +1556,11 @@ class BackupManagerService extends IBackupManager.Stub {
}
// In a full backup, we pass a null ParcelFileDescriptor as
- // the saved-state "file"
- if (!request.fullBackup) {
- savedState = ParcelFileDescriptor.open(savedStateName,
- ParcelFileDescriptor.MODE_READ_ONLY |
- ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
- }
+ // the saved-state "file". This is by definition an incremental,
+ // so we build a saved state file to pass.
+ savedState = ParcelFileDescriptor.open(savedStateName,
+ ParcelFileDescriptor.MODE_READ_ONLY |
+ ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_WRITE |
@@ -1480,7 +1574,8 @@ class BackupManagerService extends IBackupManager.Stub {
// Initiate the target's backup pass
prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
- agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
+ agent.doBackup(savedState, backupData, newState, false,
+ token, mBackupManagerBinder);
boolean success = waitUntilOperationComplete(token);
if (!success) {
@@ -1552,6 +1647,224 @@ class BackupManagerService extends IBackupManager.Stub {
}
+ // ----- Full backup to a file/socket -----
+
+ class PerformFullBackupTask implements Runnable {
+ ParcelFileDescriptor mOutputFile;
+ IFullBackupRestoreObserver mObserver;
+ boolean mIncludeApks;
+ boolean mIncludeShared;
+ boolean mAllApps;
+ String[] mPackages;
+ AtomicBoolean mLatchObject;
+ File mFilesDir;
+ File mManifestFile;
+
+ PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
+ boolean includeApks, boolean includeShared,
+ boolean doAllApps, String[] packages, AtomicBoolean latch) {
+ mOutputFile = fd;
+ mObserver = observer;
+ mIncludeApks = includeApks;
+ mIncludeShared = includeShared;
+ mAllApps = doAllApps;
+ mPackages = packages;
+ mLatchObject = latch;
+
+ mFilesDir = new File("/data/system");
+ mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
+ }
+
+ @Override
+ public void run() {
+ final List<PackageInfo> packagesToBackup;
+
+ sendStartBackup();
+
+ // doAllApps supersedes the package set if any
+ if (mAllApps) {
+ packagesToBackup = mPackageManager.getInstalledPackages(
+ PackageManager.GET_SIGNATURES);
+ } else {
+ packagesToBackup = new ArrayList<PackageInfo>();
+ for (String pkgName : mPackages) {
+ try {
+ packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
+ PackageManager.GET_SIGNATURES));
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
+ }
+ }
+ }
+
+ // Now back up the app data via the agent mechanism
+ PackageInfo pkg = null;
+ try {
+ int N = packagesToBackup.size();
+ for (int i = 0; i < N; i++) {
+ pkg = packagesToBackup.get(i);
+
+ Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
+
+ IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+ IApplicationThread.BACKUP_MODE_FULL);
+ if (agent != null) {
+ try {
+ ApplicationInfo app = mPackageManager.getApplicationInfo(
+ pkg.packageName, 0);
+ boolean sendApk = mIncludeApks
+ && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
+ && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+ (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+ sendOnBackupPackage(pkg.packageName);
+
+ {
+ BackupDataOutput output = new BackupDataOutput(
+ mOutputFile.getFileDescriptor());
+
+ if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
+ writeAppManifest(pkg, mManifestFile, sendApk);
+ FullBackup.backupToTar(pkg.packageName, null, null,
+ mFilesDir.getAbsolutePath(),
+ mManifestFile.getAbsolutePath(),
+ output);
+ }
+
+ if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
+ agent.doBackup(null, mOutputFile, null, sendApk,
+ token, mBackupManagerBinder);
+ boolean success = waitUntilOperationComplete(token);
+ if (!success) {
+ Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
+ }
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Package exists but not app info; skipping: "
+ + pkg.packageName);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error backing up " + pkg.packageName, e);
+ }
+ } else {
+ Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
+ }
+ tearDown(pkg);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "App died during full backup");
+ } finally {
+ if (pkg != null) {
+ tearDown(pkg);
+ }
+ try {
+ mOutputFile.close();
+ } catch (IOException e) {
+ /* nothing we can do about this */
+ }
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.clear();
+ }
+ synchronized (mLatchObject) {
+ mLatchObject.set(true);
+ mLatchObject.notifyAll();
+ }
+ sendEndBackup();
+ mWakelock.release();
+ if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
+ }
+ }
+
+ private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
+ throws IOException {
+ // Manifest format. All data are strings ending in LF:
+ // BACKUP_MANIFEST_VERSION, currently 1
+ //
+ // Version 1:
+ // package name
+ // package's versionCode
+ // boolean: "1" if archive includes .apk, "0" otherwise
+ // number of signatures == N
+ // N*: signature byte array in ascii format per Signature.toCharsString()
+ StringBuilder builder = new StringBuilder(4096);
+ StringBuilderPrinter printer = new StringBuilderPrinter(builder);
+
+ printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
+ printer.println(pkg.packageName);
+ printer.println(Integer.toString(pkg.versionCode));
+ printer.println(withApk ? "1" : "0");
+ if (pkg.signatures == null) {
+ printer.println("0");
+ } else {
+ printer.println(Integer.toString(pkg.signatures.length));
+ for (Signature sig : pkg.signatures) {
+ printer.println(sig.toCharsString());
+ }
+ }
+
+ FileOutputStream outstream = new FileOutputStream(manifestFile);
+ Libcore.os.ftruncate(outstream.getFD(), 0);
+ outstream.write(builder.toString().getBytes());
+ outstream.close();
+ }
+
+ private void tearDown(PackageInfo pkg) {
+ final ApplicationInfo app = pkg.applicationInfo;
+ try {
+ // unbind and tidy up even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(app);
+
+ // The agent was running with a stub Application object, so shut it down
+ if (app.uid != Process.SYSTEM_UID) {
+ if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
+ }
+
+ // wrappers for observer use
+ void sendStartBackup() {
+ if (mObserver != null) {
+ try {
+ mObserver.onStartBackup();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: startBackup");
+ mObserver = null;
+ }
+ }
+ }
+
+ void sendOnBackupPackage(String name) {
+ if (mObserver != null) {
+ try {
+ // TODO: use a more user-friendly name string
+ mObserver.onBackupPackage(name);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: backupPackage");
+ mObserver = null;
+ }
+ }
+ }
+
+ void sendEndBackup() {
+ if (mObserver != null) {
+ try {
+ mObserver.onEndBackup();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: endBackup");
+ mObserver = null;
+ }
+ }
+ }
+ }
+
+
// ----- Restore handling -----
private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
@@ -1893,7 +2206,7 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor backupData = null;
ParcelFileDescriptor newState = null;
- int token = mTokenGenerator.nextInt();
+ final int token = generateToken();
try {
// Run the transport's restore pass
backupData = ParcelFileDescriptor.open(backupDataName,
@@ -1957,7 +2270,9 @@ class BackupManagerService extends IBackupManager.Stub {
try { if (backupData != null) backupData.close(); } catch (IOException e) {}
try { if (newState != null) newState.close(); } catch (IOException e) {}
backupData = newState = null;
- mCurrentOperations.delete(token);
+ synchronized (mCurrentOperations) {
+ mCurrentOperations.delete(token);
+ }
// If we know a priori that we'll need to perform a full post-restore backup
// pass, clear the new state file data. This means we're discarding work that
@@ -2092,7 +2407,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (app.packageName.equals(packageName)) {
// Add the caller to the set of pending backups. If there is
// one already there, then overwrite it, but no harm done.
- BackupRequest req = new BackupRequest(app, false);
+ BackupRequest req = new BackupRequest(app);
if (mPendingBackups.put(app.packageName, req) == null) {
// Journal this request in case of crash. The put()
// operation returned null when this package was not already
@@ -2239,10 +2554,141 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // Run a *full* backup pass for the given package, writing the resulting data stream
+ // to the supplied file descriptor. This method is synchronous and does not return
+ // to the caller until the backup has been completed.
+ public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
+ boolean doAllApps, String[] pkgList) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+
+ // Validate
+ if (!doAllApps) {
+ if (!includeShared) {
+ // If we're backing up shared data (sdcard or equivalent), then we can run
+ // without any supplied app names. Otherwise, we'd be doing no work, so
+ // report the error.
+ if (pkgList == null || pkgList.length == 0) {
+ throw new IllegalArgumentException(
+ "Backup requested but neither shared nor any apps named");
+ }
+ }
+ }
+
+ if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
+ + " shared=" + includeShared + " all=" + doAllApps
+ + " pkgs=" + pkgList);
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
+ doAllApps, pkgList);
+ final int token = generateToken();
+ synchronized (mFullConfirmations) {
+ mFullConfirmations.put(token, params);
+ }
+
+ // start up the confirmation UI, making sure the screen lights up
+ if (DEBUG) Slog.d(TAG, "Starting confirmation UI, token=" + token);
+ try {
+ Intent confIntent = new Intent(FullBackup.FULL_BACKUP_INTENT_ACTION);
+ confIntent.setClassName("com.android.backupconfirm",
+ "com.android.backupconfirm.BackupRestoreConfirmation");
+ confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
+ confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(confIntent);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "Unable to launch full backup confirmation", e);
+ mFullConfirmations.delete(token);
+ return;
+ }
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+
+ // start the confirmation countdown
+ if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
+ + TIMEOUT_FULL_CONFIRMATION + " millis");
+ Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
+ token, 0, params);
+ mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
+
+ // wait for the backup to be performed
+ if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
+ waitForCompletion(params);
+ if (DEBUG) Slog.d(TAG, "...Full backup operation complete!");
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ try {
+ fd.close();
+ } catch (IOException e) {
+ // just eat it
+ }
+ }
+ }
+
+ void waitForCompletion(FullParams params) {
+ synchronized (params.latch) {
+ while (params.latch.get() == false) {
+ try {
+ params.latch.wait();
+ } catch (InterruptedException e) { /* never interrupted */ }
+ }
+ }
+ }
+
+ void signalFullBackupRestoreCompletion(FullParams params) {
+ synchronized (params.latch) {
+ params.latch.set(true);
+ params.latch.notifyAll();
+ }
+ }
+
+ // Confirm that the previously-requested full backup/restore operation can proceed. This
+ // is used to require a user-facing disclosure about the operation.
+ public void acknowledgeFullBackupOrRestore(int token, boolean allow,
+ IFullBackupRestoreObserver observer) {
+ if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
+ + " allow=" + allow);
+
+ // TODO: possibly require not just this signature-only permission, but even
+ // require that the specific designated confirmation-UI app uid is the caller?
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+
+ FullParams params;
+ synchronized (mFullConfirmations) {
+ params = mFullConfirmations.get(token);
+ if (params != null) {
+ mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
+ mFullConfirmations.delete(token);
+
+ if (allow) {
+ params.observer = observer;
+ final int verb = params instanceof FullBackupParams
+ ? MSG_RUN_FULL_BACKUP
+ : MSG_RUN_FULL_RESTORE;
+
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(verb, params);
+ mBackupHandler.sendMessage(msg);
+ } else {
+ Slog.w(TAG, "User rejected full backup/restore operation");
+ // indicate completion without having actually transferred any data
+ signalFullBackupRestoreCompletion(params);
+ }
+ } else {
+ Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
// Enable/disable the backup service
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupEnabled");
+ "setBackupEnabled");
Slog.i(TAG, "Backup enabled => " + enable);
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 47599c8..1aff9a2 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -390,7 +390,7 @@ class BatteryService extends Binder {
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- if (true) {
+ if (false) {
Slog.d(TAG, "level:" + mBatteryLevel +
" scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
" health:" + mBatteryHealth + " present:" + mBatteryPresent +
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 6780b03..db831c7 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -41,6 +41,7 @@ import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -425,7 +426,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- mTethering = new Tethering(mContext, mHandler.getLooper());
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
+
+ mTethering = new Tethering(mContext, nmService, mHandler.getLooper());
mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
!mTethering.isDunRequired()) &&
(mTethering.getTetherableUsbRegexs().length != 0 ||
@@ -1381,13 +1385,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
addDefaultRoute(mNetTrackers[netType]);
} else {
// many radios add a default route even when we don't want one.
- // remove the default interface unless we need it for our active network
+ // remove the default route unless we need it for our active network
if (mActiveDefaultNetwork != -1) {
- LinkProperties linkProperties =
+ LinkProperties defaultLinkProperties =
mNetTrackers[mActiveDefaultNetwork].getLinkProperties();
LinkProperties newLinkProperties =
mNetTrackers[netType].getLinkProperties();
- String defaultIface = linkProperties.getInterfaceName();
+ String defaultIface = defaultLinkProperties.getInterfaceName();
if (defaultIface != null &&
!defaultIface.equals(newLinkProperties.getInterfaceName())) {
removeDefaultRoute(mNetTrackers[netType]);
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 0fba7c3..d34087f 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -34,7 +34,6 @@ import android.os.StatFs;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.util.Config;
import android.util.EventLog;
import android.util.Slog;
@@ -56,10 +55,10 @@ import android.util.Slog;
* settings parameter with a default value of 2MB), the free memory is
* logged to the event log.
*/
-class DeviceStorageMonitorService extends Binder {
+public class DeviceStorageMonitorService extends Binder {
private static final String TAG = "DeviceStorageMonitorService";
private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean localLOGV = false;
private static final int DEVICE_MEMORY_WHAT = 1;
private static final int MONITOR_INTERVAL = 1; //in minutes
private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
@@ -99,7 +98,7 @@ class DeviceStorageMonitorService extends Binder {
/**
* This string is used for ServiceManager access to this class.
*/
- static final String SERVICE = "devicestoragemonitor";
+ public static final String SERVICE = "devicestoragemonitor";
/**
* Handler that checks the amount of disk space on the device and sends a
@@ -398,4 +397,24 @@ class DeviceStorageMonitorService extends Binder {
// force an early check
postCheckMemoryMsg(true, 0);
}
+
+ /**
+ * Callable from other things in the system service to obtain the low memory
+ * threshold.
+ *
+ * @return low memory threshold in bytes
+ */
+ public long getMemoryLowThreshold() {
+ return mMemLowThreshold;
+ }
+
+ /**
+ * Callable from other things in the system process to check whether memory
+ * is low.
+ *
+ * @return true is memory is low
+ */
+ public boolean isMemoryLow() {
+ return mLowMemFlag;
+ }
}
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
index 0f1fc78..788a2f5 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyService.java
@@ -96,7 +96,7 @@ public class EntropyService extends Binder {
private void loadInitialEntropy() {
try {
- RandomBlock.fromFile(entropyFile).toFile(randomDevice);
+ RandomBlock.fromFile(entropyFile).toFile(randomDevice, false);
} catch (IOException e) {
Slog.w(TAG, "unable to load initial entropy (first boot?)", e);
}
@@ -104,7 +104,7 @@ public class EntropyService extends Binder {
private void writeEntropy() {
try {
- RandomBlock.fromFile(randomDevice).toFile(entropyFile);
+ RandomBlock.fromFile(randomDevice).toFile(entropyFile, true);
} catch (IOException e) {
Slog.w(TAG, "unable to write entropy", e);
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 1455764..7028772 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1399,6 +1399,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ public InputMethodSubtype getLastInputMethodSubtype() {
+ synchronized (mMethodMap) {
+ final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
+ // TODO: Handle the case of the last IME with no subtypes
+ if (lastIme == null || TextUtils.isEmpty(lastIme.first)
+ || TextUtils.isEmpty(lastIme.second)) return null;
+ final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
+ if (lastImi == null) return null;
+ try {
+ final int lastSubtypeHash = Integer.valueOf(lastIme.second);
+ return lastImi.getSubtypeAt(getSubtypeIdFromHashCode(
+ lastImi, lastSubtypeHash));
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+ }
+
private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
if (token == null) {
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index b78389b..1d3e3ac 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -35,7 +35,6 @@ import android.util.Slog;
import android.util.LogPrinter;
import android.util.Printer;
-import android.util.Config;
import android.content.Intent;
import android.content.IntentFilter;
@@ -45,7 +44,7 @@ import android.content.IntentFilter;
public class IntentResolver<F extends IntentFilter, R extends Object> {
final private static String TAG = "IntentResolver";
final private static boolean DEBUG = false;
- final private static boolean localLOGV = DEBUG || Config.LOGV;
+ final private static boolean localLOGV = DEBUG || false;
public void addFilter(F f) {
if (localLOGV) {
diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/LightsService.java
index 21f2bcf..1e95f3e 100644
--- a/services/java/com/android/server/LightsService.java
+++ b/services/java/com/android/server/LightsService.java
@@ -148,7 +148,6 @@ public class LightsService {
fis.close();
return (result != '0');
} catch (Exception e) {
- Slog.e(TAG, "getFlashlightEnabled failed", e);
return false;
}
}
@@ -168,7 +167,7 @@ public class LightsService {
fos.write(bytes);
fos.close();
} catch (Exception e) {
- Slog.e(TAG, "setFlashlightEnabled failed", e);
+ // fail silently
}
}
};
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 6c2f8d1..c18ccc8 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -18,6 +18,7 @@ package com.android.server;
import com.android.internal.app.IMediaContainerService;
import com.android.server.am.ActivityManagerService;
+import com.android.server.pm.PackageManagerService;
import android.Manifest;
import android.content.BroadcastReceiver;
@@ -87,6 +88,9 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
private static final String VOLD_TAG = "VoldConnector";
+ /** Maximum number of ASEC containers allowed to be mounted. */
+ private static final int MAX_CONTAINERS = 250;
+
/*
* Internal vold volume state constants
*/
@@ -510,7 +514,6 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
}
}
};
-
private final class MountServiceBinderListener implements IBinder.DeathRecipient {
final IMountServiceListener mListener;
@@ -1112,8 +1115,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
* amount of containers we'd ever expect to have. This keeps an
* "asec list" from blocking a thread repeatedly.
*/
- mConnector = new NativeDaemonConnector(this, "vold",
- PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
+ mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG);
mReady = false;
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 44f5df2..d931350 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.pm.PackageManager;
+import android.net.NetworkStats;
import android.net.Uri;
import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
@@ -32,6 +33,8 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.INetworkManagementService;
import android.os.Handler;
+import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -44,13 +47,21 @@ import android.content.ContentResolver;
import android.database.ContentObserver;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.RandomAccessFile;
+import java.io.Reader;
import java.lang.IllegalStateException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.CountDownLatch;
+import libcore.io.IoUtils;
+
/**
* @hide
*/
@@ -716,12 +727,47 @@ class NetworkManagementService extends INetworkManagementService.Stub {
return -1;
}
- public long getInterfaceRxCounter(String iface) {
- return getInterfaceCounter(iface, true);
+ /** {@inheritDoc} */
+ public NetworkStats getNetworkStatsSummary() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ final String[] ifaces = listInterfaces();
+ final NetworkStats.Builder stats = new NetworkStats.Builder(
+ SystemClock.elapsedRealtime(), ifaces.length);
+
+ for (String iface : ifaces) {
+ final long rx = getInterfaceCounter(iface, true);
+ final long tx = getInterfaceCounter(iface, false);
+ stats.addEntry(iface, NetworkStats.UID_ALL, rx, tx);
+ }
+
+ return stats.build();
}
- public long getInterfaceTxCounter(String iface) {
- return getInterfaceCounter(iface, false);
+ /** {@inheritDoc} */
+ public NetworkStats getNetworkStatsDetail() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ final File procPath = new File("/proc/uid_stat");
+ final String[] knownUids = procPath.list();
+ final NetworkStats.Builder stats = new NetworkStats.Builder(
+ SystemClock.elapsedRealtime(), knownUids.length);
+
+ // TODO: kernel module will provide interface-level stats in future
+ // TODO: migrate these stats to come across netd in bulk, instead of all
+ // these individual file reads.
+ for (String uid : knownUids) {
+ final File uidPath = new File(procPath, uid);
+ final int rx = readSingleIntFromFile(new File(uidPath, "tcp_rcv"));
+ final int tx = readSingleIntFromFile(new File(uidPath, "tcp_snd"));
+
+ final int uidInt = Integer.parseInt(uid);
+ stats.addEntry(NetworkStats.IFACE_ALL, uidInt, rx, tx);
+ }
+
+ return stats.build();
}
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
@@ -782,4 +828,24 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public int getInterfaceTxThrottle(String iface) {
return getInterfaceThrottle(iface, false);
}
+
+ /**
+ * Utility method to read a single plain-text {@link Integer} from the given
+ * {@link File}, usually from a {@code /proc/} filesystem.
+ */
+ private static int readSingleIntFromFile(File file) {
+ RandomAccessFile f = null;
+ try {
+ f = new RandomAccessFile(file, "r");
+ byte[] buffer = new byte[(int) f.length()];
+ f.readFully(buffer);
+ return Integer.parseInt(new String(buffer).trim());
+ } catch (NumberFormatException e) {
+ return -1;
+ } catch (IOException e) {
+ return -1;
+ } finally {
+ IoUtils.closeQuietly(f);
+ }
+ }
}
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 1a12a84..f693ed1 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -20,7 +20,6 @@ import static android.os.Process.*;
import android.os.Process;
import android.os.SystemClock;
-import android.util.Config;
import android.util.Slog;
import java.io.File;
@@ -35,7 +34,7 @@ import java.util.StringTokenizer;
public class ProcessStats {
private static final String TAG = "ProcessStats";
private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG || Config.LOGV;
+ private static final boolean localLOGV = DEBUG || false;
private static final int[] PROCESS_STATS_FORMAT = new int[] {
PROC_SPACE_TERM,
diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java
index cc22bd9..e5d7301 100644
--- a/services/java/com/android/server/RandomBlock.java
+++ b/services/java/com/android/server/RandomBlock.java
@@ -62,11 +62,11 @@ class RandomBlock {
return retval;
}
- void toFile(String filename) throws IOException {
+ void toFile(String filename, boolean sync) throws IOException {
if (DEBUG) Slog.v(TAG, "writing to file " + filename);
RandomAccessFile out = null;
try {
- out = new RandomAccessFile(filename, "rws");
+ out = new RandomAccessFile(filename, sync ? "rws" : "rw");
toDataOut(out);
truncateIfPossible(out);
} finally {
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 8df8177..1d2072c 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -122,6 +122,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub
// ================================================================================
// From IStatusBarService
// ================================================================================
+ public void userActivity() {
+ if (mBar != null) try {
+ mBar.userActivity();
+ } catch (RemoteException ex) {}
+ }
public void expand() {
enforceExpandStatusBar();
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 80b0174..54555bb 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -20,6 +20,7 @@ package com.android.server;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupAgentHelper;
+import android.app.backup.FullBackup;
import android.app.backup.WallpaperBackupHelper;
import android.content.Context;
import android.os.ParcelFileDescriptor;
@@ -37,12 +38,19 @@ public class SystemBackupAgent extends BackupAgentHelper {
private static final String TAG = "SystemBackupAgent";
// These paths must match what the WallpaperManagerService uses
- private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper";
- private static final String WALLPAPER_INFO = "/data/system/wallpaper_info.xml";
+ private static final String WALLPAPER_IMAGE_DIR = "/data/data/com.android.settings/files";
+ private static final String WALLPAPER_IMAGE = WALLPAPER_IMAGE_DIR + "/wallpaper";
+ private static final String WALLPAPER_INFO_DIR = "/data/system";
+ private static final String WALLPAPER_INFO = WALLPAPER_INFO_DIR + "/wallpaper_info.xml";
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
+ if (oldState == null) {
+ runFullBackup(data);
+ return;
+ }
+
// We only back up the data under the current "wallpaper" schema with metadata
WallpaperManagerService wallpaper = (WallpaperManagerService)ServiceManager.getService(
Context.WALLPAPER_SERVICE);
@@ -57,6 +65,14 @@ public class SystemBackupAgent extends BackupAgentHelper {
super.onBackup(oldState, data, newState);
}
+ private void runFullBackup(BackupDataOutput output) {
+ // Back up the data files directly
+ FullBackup.backupToTar(getPackageName(), null, null,
+ WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output);
+ FullBackup.backupToTar(getPackageName(), null, null,
+ WALLPAPER_INFO_DIR, WALLPAPER_INFO, output);
+ }
+
@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d160963..cd8915d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,7 +16,9 @@
package com.android.server;
+import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
+import com.android.server.pm.PackageManagerService;
import com.android.server.usb.UsbService;
import com.android.server.wm.WindowManagerService;
import com.android.internal.app.ShutdownThread;
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index eb14180..948118f 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -279,7 +279,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyServiceState()")){
return;
}
- Slog.i(TAG, "notifyServiceState: " + state);
synchronized (mRecords) {
mServiceState = state;
for (Record r : mRecords) {
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index d841cb3..510ff62 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -16,6 +16,9 @@
package com.android.server;
+import com.android.internal.R;
+import com.android.internal.telephony.TelephonyProperties;
+
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -30,7 +33,7 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.INetworkManagementEventObserver;
import android.net.IThrottleManager;
-import android.net.SntpClient;
+import android.net.NetworkStats;
import android.net.ThrottleManager;
import android.os.Binder;
import android.os.Environment;
@@ -47,10 +50,9 @@ import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.NtpTrustedTime;
import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.internal.telephony.TelephonyProperties;
+import android.util.TrustedTime;
import java.io.BufferedWriter;
import java.io.File;
@@ -60,11 +62,11 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
import java.util.GregorianCalendar;
import java.util.Properties;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
// TODO - add comments - reference the ThrottleManager for public API
public class ThrottleService extends IThrottleManager.Stub {
@@ -84,6 +86,11 @@ public class ThrottleService extends IThrottleManager.Stub {
private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
+ private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000;
+ private static final long MAX_NTP_FETCH_WAIT = 20 * 1000;
+
+ private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE;
+
private int mPolicyPollPeriodSec;
private AtomicLong mPolicyThreshold;
private AtomicInteger mPolicyThrottleValue;
@@ -121,10 +128,24 @@ public class ThrottleService extends IThrottleManager.Stub {
private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
private static final String PROPERTIES_FILE = "/etc/gps.conf";
- private String mNtpServer;
- private boolean mNtpActive;
+
+ private Intent mPollStickyBroadcast;
+
+ private TrustedTime mTime;
+
+ private static INetworkManagementService getNetworkManagementService() {
+ final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ return INetworkManagementService.Stub.asInterface(b);
+ }
public ThrottleService(Context context) {
+ // TODO: move to using cached NtpTrustedTime
+ this(context, getNetworkManagementService(), new NtpTrustedTime(),
+ context.getResources().getString(R.string.config_datause_iface));
+ }
+
+ public ThrottleService(Context context, INetworkManagementService nmService, TrustedTime time,
+ String iface) {
if (VDBG) Slog.v(TAG, "Starting ThrottleService");
mContext = context;
@@ -132,17 +153,15 @@ public class ThrottleService extends IThrottleManager.Stub {
mPolicyThrottleValue = new AtomicInteger();
mThrottleIndex = new AtomicInteger();
- mNtpActive = false;
-
- mIface = mContext.getResources().getString(R.string.config_datause_iface);
+ mIface = iface;
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
Intent pollIntent = new Intent(ACTION_POLL, null);
mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
Intent resetIntent = new Intent(ACTION_RESET, null);
mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- mNMService = INetworkManagementService.Stub.asInterface(b);
+ mNMService = nmService;
+ mTime = time;
mNotificationManager = (NotificationManager)mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
@@ -189,7 +208,7 @@ public class ThrottleService extends IThrottleManager.Stub {
mMsg = msg;
}
- void observe(Context context) {
+ void register(Context context) {
ContentResolver resolver = context.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.THROTTLE_POLLING_SEC), false, this);
@@ -207,6 +226,11 @@ public class ThrottleService extends IThrottleManager.Stub {
Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
}
+ void unregister(Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ resolver.unregisterContentObserver(this);
+ }
+
@Override
public void onChange(boolean selfChange) {
mHandler.obtainMessage(mMsg).sendToTarget();
@@ -220,7 +244,9 @@ public class ThrottleService extends IThrottleManager.Stub {
}
private long ntpToWallTime(long ntpTime) {
- long bestNow = getBestTime(true); // do it quickly
+ // get time quickly without worrying about trusted state
+ long bestNow = mTime.hasCache() ? mTime.currentTimeMillis()
+ : System.currentTimeMillis();
long localNow = System.currentTimeMillis();
return localNow + (ntpTime - bestNow);
}
@@ -300,7 +326,7 @@ public class ThrottleService extends IThrottleManager.Stub {
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
+ dispatchPoll();
}
}, new IntentFilter(ACTION_POLL));
@@ -308,7 +334,7 @@ public class ThrottleService extends IThrottleManager.Stub {
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
+ dispatchReset();
}
}, new IntentFilter(ACTION_RESET));
@@ -318,7 +344,10 @@ public class ThrottleService extends IThrottleManager.Stub {
File file = new File(PROPERTIES_FILE);
stream = new FileInputStream(file);
properties.load(stream);
- mNtpServer = properties.getProperty("NTP_SERVER", null);
+ final String ntpServer = properties.getProperty("NTP_SERVER", null);
+ if (mTime instanceof NtpTrustedTime) {
+ ((NtpTrustedTime) mTime).setNtpServer(ntpServer, MAX_NTP_FETCH_WAIT);
+ }
} catch (IOException e) {
Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
} finally {
@@ -343,9 +372,33 @@ public class ThrottleService extends IThrottleManager.Stub {
}
mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
- mSettingsObserver.observe(mContext);
+ mSettingsObserver.register(mContext);
+ }
+
+ void shutdown() {
+ // TODO: eventually connect with ShutdownThread to persist stats during
+ // graceful shutdown.
+
+ if (mThread != null) {
+ mThread.quit();
+ }
+
+ if (mSettingsObserver != null) {
+ mSettingsObserver.unregister(mContext);
+ }
+
+ if (mPollStickyBroadcast != null) {
+ mContext.removeStickyBroadcast(mPollStickyBroadcast);
+ }
}
+ void dispatchPoll() {
+ mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
+ }
+
+ void dispatchReset() {
+ mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
+ }
private static final int EVENT_REBOOT_RECOVERY = 0;
private static final int EVENT_POLICY_CHANGED = 1;
@@ -440,15 +493,17 @@ public class ThrottleService extends IThrottleManager.Stub {
mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
- mMaxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC, MAX_NTP_CACHE_AGE_SEC);
+ final int maxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC,
+ (int) (MAX_NTP_CACHE_AGE / 1000));
+ mMaxNtpCacheAge = maxNtpCacheAgeSec * 1000;
if (VDBG || (mPolicyThreshold.get() != 0)) {
Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
- ", noteType=" + mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" +
- mMaxNtpCacheAgeSec);
+ ", noteType=" + mPolicyNotificationsAllowedMask + ", mMaxNtpCacheAge=" +
+ mMaxNtpCacheAge);
}
// force updates
@@ -464,15 +519,30 @@ public class ThrottleService extends IThrottleManager.Stub {
private void onPollAlarm() {
long now = SystemClock.elapsedRealtime();
- long next = now + mPolicyPollPeriodSec*1000;
+ long next = now + mPolicyPollPeriodSec * 1000;
- checkForAuthoritativeTime();
+ // when trusted cache outdated, try refreshing
+ if (mTime.getCacheAge() > mMaxNtpCacheAge) {
+ if (mTime.forceRefresh()) {
+ if (VDBG) Slog.d(TAG, "updated trusted time, reseting alarm");
+ dispatchReset();
+ }
+ }
long incRead = 0;
long incWrite = 0;
try {
- incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
- incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
+ final NetworkStats stats = mNMService.getNetworkStatsSummary();
+ final int index = stats.findIndex(mIface, NetworkStats.UID_ALL);
+
+ if (index != -1) {
+ incRead = stats.rx[index] - mLastRead;
+ incWrite = stats.tx[index] - mLastWrite;
+ } else {
+ // missing iface, assume stats are 0
+ Slog.w(TAG, "unable to find stats for iface " + mIface);
+ }
+
// handle iface resets - on some device the 3g iface comes and goes and gets
// totals reset to 0. Deal with it
if ((incRead < 0) || (incWrite < 0)) {
@@ -509,6 +579,7 @@ public class ThrottleService extends IThrottleManager.Stub {
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
mContext.sendStickyBroadcast(broadcast);
+ mPollStickyBroadcast = broadcast;
mAlarmManager.cancel(mPendingPollIntent);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
@@ -538,7 +609,8 @@ public class ThrottleService extends IThrottleManager.Stub {
// have we spoken with an ntp server yet?
// this is controversial, but we'd rather err towards not throttling
- if ((mNtpServer != null) && !mNtpActive) {
+ if (!mTime.hasCache()) {
+ Slog.w(TAG, "missing trusted time, skipping throttle check");
return;
}
@@ -697,9 +769,15 @@ public class ThrottleService extends IThrottleManager.Stub {
" bytes read and " + mRecorder.getPeriodTx(0) + " written");
}
- long now = getBestTime(false);
+ // when trusted cache outdated, try refreshing
+ if (mTime.getCacheAge() > mMaxNtpCacheAge) {
+ mTime.forceRefresh();
+ }
- if (mNtpActive || (mNtpServer == null)) {
+ // as long as we have a trusted time cache, we always reset alarms,
+ // even if the refresh above failed.
+ if (mTime.hasCache()) {
+ final long now = mTime.currentTimeMillis();
Calendar end = calculatePeriodEnd(now);
Calendar start = calculatePeriodStart(end);
@@ -714,56 +792,11 @@ public class ThrottleService extends IThrottleManager.Stub {
SystemClock.elapsedRealtime() + offset,
mPendingResetIntent);
} else {
- if (VDBG) Slog.d(TAG, "no authoritative time - not resetting period");
+ if (VDBG) Slog.d(TAG, "no trusted time, not resetting period");
}
}
}
- private void checkForAuthoritativeTime() {
- if (mNtpActive || (mNtpServer == null)) return;
-
- // will try to get the ntp time and switch to it if found.
- // will also cache the time so we don't fetch it repeatedly.
- getBestTime(false);
- }
-
- private static final int MAX_NTP_CACHE_AGE_SEC = 60 * 60 * 24; // 1 day
- private static final int MAX_NTP_FETCH_WAIT = 20 * 1000;
- private int mMaxNtpCacheAgeSec = MAX_NTP_CACHE_AGE_SEC;
- private long cachedNtp;
- private long cachedNtpTimestamp;
-
- // if the request is tied to UI and ANR's are a danger, request a fast result
- // the regular polling should have updated the cached time recently using the
- // slower method (!fast)
- private long getBestTime(boolean fast) {
- if (mNtpServer != null) {
- if (mNtpActive) {
- long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp;
- if (ntpAge < mMaxNtpCacheAgeSec * 1000 || fast) {
- if (VDBG) Slog.v(TAG, "using cached time");
- return cachedNtp + ntpAge;
- }
- }
- SntpClient client = new SntpClient();
- if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT)) {
- cachedNtp = client.getNtpTime();
- cachedNtpTimestamp = SystemClock.elapsedRealtime();
- if (!mNtpActive) {
- mNtpActive = true;
- if (VDBG) Slog.d(TAG, "found Authoritative time - reseting alarm");
- mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
- }
- if (VDBG) Slog.v(TAG, "using Authoritative time: " + cachedNtp);
- return cachedNtp;
- }
- }
- long time = System.currentTimeMillis();
- if (VDBG) Slog.v(TAG, "using User time: " + time);
- mNtpActive = false;
- return time;
- }
-
// records bytecount data for a given time and accumulates it into larger time windows
// for logging and other purposes
//
@@ -929,7 +962,7 @@ public class ThrottleService extends IThrottleManager.Stub {
private void checkAndDeleteLRUDataFile(File dir) {
File[] files = dir.listFiles();
- if (files.length <= MAX_SIMS_SUPPORTED) return;
+ if (files == null || files.length <= MAX_SIMS_SUPPORTED) return;
if (DBG) Slog.d(TAG, "Too many data files");
do {
File oldest = null;
@@ -949,9 +982,11 @@ public class ThrottleService extends IThrottleManager.Stub {
File newest = null;
File[] files = dir.listFiles();
- for (File f : files) {
- if ((newest == null) || (newest.lastModified() < f.lastModified())) {
- newest = f;
+ if (files != null) {
+ for (File f : files) {
+ if ((newest == null) || (newest.lastModified() < f.lastModified())) {
+ newest = f;
+ }
}
}
if (newest == null) {
@@ -1126,7 +1161,7 @@ public class ThrottleService extends IThrottleManager.Stub {
" seconds.");
pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
pw.println("Current Throttle Index is " + mThrottleIndex.get());
- pw.println("Max NTP Cache Age is " + mMaxNtpCacheAgeSec);
+ pw.println("mMaxNtpCacheAge=" + mMaxNtpCacheAge);
for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index b1a6a9a..c129b97 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -49,6 +49,7 @@ import android.service.wallpaper.IWallpaperService;
import android.service.wallpaper.WallpaperService;
import android.util.Slog;
import android.util.Xml;
+import android.view.Display;
import android.view.IWindowManager;
import android.view.WindowManager;
@@ -726,6 +727,17 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
mHeight = -1;
mName = "";
}
+
+ // We always want to have some reasonable width hint.
+ WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+ Display d = wm.getDefaultDisplay();
+ int baseSize = d.getMaximumSizeDimension();
+ if (mWidth < baseSize) {
+ mWidth = baseSize;
+ }
+ if (mHeight < baseSize) {
+ mHeight = baseSize;
+ }
}
// Called by SystemBackupAgent after files are restored to disk.
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 2a25c2a..2d3ac00 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -33,7 +33,6 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -45,7 +44,7 @@ import java.util.Calendar;
/** This class calls its monitor every minute. Killing this process if they don't return **/
public class Watchdog extends Thread {
static final String TAG = "Watchdog";
- static final boolean localLOGV = false || Config.LOGV;
+ static final boolean localLOGV = false || false;
// Set this to true to use debug default values.
static final boolean DB = false;
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 0000237..a2d10df 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -235,6 +235,15 @@ public class WifiService extends IWifiManager.Stub {
}
break;
}
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+ Slog.d(TAG, "Send failed, client connection lost");
+ } else {
+ Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
+ }
+ mClients.remove((AsyncChannel) msg.obj);
+ break;
+ }
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
AsyncChannel ac = new AsyncChannel();
ac.connect(mContext, this, msg.replyTo);
@@ -928,7 +937,7 @@ public class WifiService extends IWifiManager.Stub {
evaluateTrafficStatsPolling();
mWifiStateMachine.enableRssiPolling(true);
if (mBackgroundScanSupported) {
- mWifiStateMachine.enableBackgroundScan(false);
+ mWifiStateMachine.enableBackgroundScanCommand(false);
}
mWifiStateMachine.enableAllNetworks();
updateWifiState();
@@ -940,7 +949,7 @@ public class WifiService extends IWifiManager.Stub {
evaluateTrafficStatsPolling();
mWifiStateMachine.enableRssiPolling(false);
if (mBackgroundScanSupported) {
- mWifiStateMachine.enableBackgroundScan(true);
+ mWifiStateMachine.enableBackgroundScanCommand(true);
}
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 94531bb..6ef6963 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -28,12 +28,12 @@ import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Config;
import android.util.Slog;
import java.io.IOException;
@@ -73,8 +73,8 @@ import java.util.Random;
*/
public class WifiWatchdogService {
private static final String TAG = "WifiWatchdogService";
- private static final boolean V = false || Config.LOGV;
- private static final boolean D = true || Config.LOGD;
+ private static final boolean V = false || false;
+ private static final boolean D = true || false;
private Context mContext;
private ContentResolver mContentResolver;
@@ -509,6 +509,7 @@ public class WifiWatchdogService {
// This access point does not require a watchdog, so queue idle on the main thread
mHandler.idle();
}
+ mHandler.checkWalledGarden(ssid);
}
/**
@@ -1003,6 +1004,8 @@ public class WifiWatchdogService {
* The object will be an {@link AccessPoint}.
*/
static final int ACTION_BACKGROUND_CHECK_AP = 3;
+ /** Check whether the connection is a walled garden */
+ static final int ACTION_CHECK_WALLED_GARDEN = 4;
/**
* Go to sleep for the current network. We are conservative with making
@@ -1023,7 +1026,16 @@ public class WifiWatchdogService {
static final int MESSAGE_DISCONNECTED = 104;
/** Performs a hard-reset on the watchdog state. */
static final int MESSAGE_RESET = 105;
-
+
+ /* Walled garden detection */
+ private String mLastSsid;
+ private long mLastTime;
+ private final long MIN_WALLED_GARDEN_TEST_INTERVAL = 15 * 60 * 1000; //15 minutes
+
+ void checkWalledGarden(String ssid) {
+ sendMessage(obtainMessage(ACTION_CHECK_WALLED_GARDEN, ssid));
+ }
+
void checkAp(AccessPoint ap) {
removeAllActions();
sendMessage(obtainMessage(ACTION_CHECK_AP, ap));
@@ -1085,6 +1097,9 @@ public class WifiWatchdogService {
case ACTION_BACKGROUND_CHECK_AP:
handleBackgroundCheckAp((AccessPoint) msg.obj);
break;
+ case ACTION_CHECK_WALLED_GARDEN:
+ handleWalledGardenCheck((String) msg.obj);
+ break;
case MESSAGE_SLEEP:
handleSleep((String) msg.obj);
break;
@@ -1102,6 +1117,43 @@ public class WifiWatchdogService {
break;
}
}
+
+ private boolean isWalledGardenConnection() {
+ //One way to detect a walled garden is to see if multiple DNS queries
+ //resolve to the same IP address
+ try {
+ String host1 = "www.google.com";
+ String host2 = "www.android.com";
+ String address1 = InetAddress.getByName(host1).getHostAddress();
+ String address2 = InetAddress.getByName(host2).getHostAddress();
+ if (address1.equals(address2)) return true;
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ return false;
+ }
+
+ private void handleWalledGardenCheck(String ssid) {
+ long currentTime = System.currentTimeMillis();
+ //Avoid a walled garden test on the same network if one was already done
+ //within MIN_WALLED_GARDEN_TEST_INTERVAL. This will handle scenarios where
+ //there are frequent network disconnections
+ if (ssid.equals(mLastSsid) &&
+ (currentTime - mLastTime) < MIN_WALLED_GARDEN_TEST_INTERVAL) {
+ return;
+ }
+
+ mLastTime = currentTime;
+ mLastSsid = ssid;
+
+ if (isWalledGardenConnection()) {
+ Uri uri = Uri.parse("http://www.google.com");
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+ }
}
/**
@@ -1264,7 +1316,7 @@ public class WifiWatchdogService {
return false;
} catch (Exception e) {
- if (V || Config.LOGD) {
+ if (V || false) {
Slog.d(TAG, "DnsPinger.isReachable got an unknown exception", e);
}
return false;
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
new file mode 100644
index 0000000..8ba0a0b
--- /dev/null
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import com.android.server.wm.InputFilter;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+
+/**
+ * Input filter for accessibility.
+ *
+ * Currently just a stub but will eventually implement touch exploration, etc.
+ */
+public class AccessibilityInputFilter extends InputFilter {
+ private static final String TAG = "AccessibilityInputFilter";
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+
+ /**
+ * This is an interface for explorers that take a {@link MotionEvent}
+ * stream and perform touch exploration of the screen content.
+ */
+ public interface Explorer {
+ /**
+ * Handles a {@link MotionEvent}.
+ *
+ * @param event The event to handle.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ public void onMotionEvent(MotionEvent event, int policyFlags);
+
+ /**
+ * Requests that the explorer clears its internal state.
+ *
+ * @param event The last received event.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ public void clear(MotionEvent event, int policyFlags);
+ }
+
+ private TouchExplorer mTouchExplorer;
+ private int mTouchscreenSourceDeviceId;
+
+ public AccessibilityInputFilter(Context context) {
+ super(context.getMainLooper());
+ mContext = context;
+ }
+
+ @Override
+ public void onInstalled() {
+ if (DEBUG) {
+ Slog.d(TAG, "Accessibility input filter installed.");
+ }
+ super.onInstalled();
+ }
+
+ @Override
+ public void onUninstalled() {
+ if (DEBUG) {
+ Slog.d(TAG, "Accessibility input filter uninstalled.");
+ }
+ super.onUninstalled();
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event, int policyFlags) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ + Integer.toHexString(policyFlags));
+ }
+ if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ int deviceId = event.getDeviceId();
+ if (mTouchscreenSourceDeviceId != deviceId) {
+ mTouchscreenSourceDeviceId = deviceId;
+ if (mTouchExplorer != null) {
+ mTouchExplorer.clear(motionEvent, policyFlags);
+ } else {
+ mTouchExplorer = new TouchExplorer(this, mContext);
+ }
+ }
+ if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) != 0) {
+ mTouchExplorer.onMotionEvent(motionEvent, policyFlags);
+ } else {
+ mTouchExplorer.clear(motionEvent, policyFlags);
+ }
+ } else {
+ super.onInputEvent(event, policyFlags);
+ }
+ }
+}
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 04ae490..d96369b 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -14,11 +14,12 @@
** limitations under the License.
*/
-package com.android.server;
+package com.android.server.accessibility;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.wm.WindowManagerService;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -43,10 +44,10 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Config;
import android.util.Slog;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
@@ -55,6 +56,7 @@ import android.view.accessibility.IAccessibilityManagerClient;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -73,6 +75,8 @@ import java.util.Set;
public class AccessibilityManagerService extends IAccessibilityManager.Stub
implements HandlerCaller.Callback {
+ private static final boolean DEBUG = false;
+
private static final String LOG_TAG = "AccessibilityManagerService";
private static int sIdCounter = 0;
@@ -101,11 +105,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
+ private final SparseArray<List<ServiceInfo>> mFeedbackTypeToEnabledServicesMap =
+ new SparseArray<List<ServiceInfo>>();
+
private PackageManager mPackageManager;
private int mHandledFeedbackTypes = 0;
private boolean mIsEnabled;
+ private AccessibilityInputFilter mInputFilter;
/**
* Handler for delayed event dispatch.
@@ -131,7 +139,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*
* @param context A {@link Context} instance.
*/
- AccessibilityManagerService(Context context) {
+ public AccessibilityManagerService(Context context) {
mContext = context;
mPackageManager = mContext.getPackageManager();
mCaller = new HandlerCaller(context, this);
@@ -296,6 +304,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ public List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
+ synchronized (mLock) {
+ List<ServiceInfo> enabledServices = mFeedbackTypeToEnabledServicesMap.get(feedbackType);
+ if (enabledServices == null) {
+ return Collections.emptyList();
+ }
+ return enabledServices;
+ }
+ }
+
public void interrupt() {
synchronized (mLock) {
for (int i = 0, count = mServices.size(); i < count; i++) {
@@ -335,6 +353,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
service.mNotificationTimeout = info.notificationTimeout;
service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+
+ updateStateOnEnabledService(service);
}
return;
default:
@@ -445,7 +465,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
try {
listener.onAccessibilityEvent(event);
- if (Config.DEBUG) {
+ if (DEBUG) {
Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
}
} catch (RemoteException re) {
@@ -465,10 +485,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @return True if the service was removed, false otherwise.
*/
private boolean removeDeadServiceLocked(Service service) {
- if (Config.DEBUG) {
+ if (DEBUG) {
Slog.i(LOG_TAG, "Dead service " + service.mService + " removed");
}
mHandler.removeMessages(service.mId);
+ updateStateOnDisabledService(service);
return mServices.remove(service);
}
@@ -589,7 +610,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (isEnabled) {
if (enabledServices.contains(componentName)) {
if (service == null) {
- service = new Service(componentName);
+ service = new Service(componentName, intalledService);
}
service.bind();
} else if (!enabledServices.contains(componentName)) {
@@ -621,6 +642,68 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
+ * Sets the input filter state. If the filter is in enabled it is registered
+ * in the window manager, otherwise the filter is removed from the latter.
+ *
+ * @param enabled Whether the input filter is enabled.
+ */
+ private void setInputFilterEnabledLocked(boolean enabled) {
+ WindowManagerService wm = (WindowManagerService)ServiceManager.getService(
+ Context.WINDOW_SERVICE);
+ if (wm != null) {
+ if (enabled) {
+ if (mInputFilter == null) {
+ mInputFilter = new AccessibilityInputFilter(mContext);
+ }
+ wm.setInputFilter(mInputFilter);
+ } else {
+ wm.setInputFilter(null);
+ }
+ }
+ }
+
+ /**
+ * Updates the set of enabled services for a given feedback type and
+ * if more than one of them provides spoken feedback enables touch
+ * exploration.
+ *
+ * @param service An enable service.
+ */
+ private void updateStateOnEnabledService(Service service) {
+ int feedbackType = service.mFeedbackType;
+ List<ServiceInfo> enabledServices = mFeedbackTypeToEnabledServicesMap.get(feedbackType);
+ if (enabledServices == null) {
+ enabledServices = new ArrayList<ServiceInfo>();
+ mFeedbackTypeToEnabledServicesMap.put(feedbackType, enabledServices);
+ }
+ enabledServices.add(service.mServiceInfo);
+
+ // We enable touch exploration if at least one
+ // enabled service provides spoken feedback.
+ if (enabledServices.size() > 0
+ && service.mFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) {
+ updateClientsLocked();
+ setInputFilterEnabledLocked(true);
+ }
+ }
+
+ private void updateStateOnDisabledService(Service service) {
+ List<ServiceInfo> enabledServices =
+ mFeedbackTypeToEnabledServicesMap.get(service.mFeedbackType);
+ if (enabledServices == null) {
+ return;
+ }
+ enabledServices.remove(service.mServiceInfo);
+ // We disable touch exploration if no
+ // enabled service provides spoken feedback.
+ if (enabledServices.isEmpty()
+ && service.mFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) {
+ updateClientsLocked();
+ setInputFilterEnabledLocked(false);
+ }
+ }
+
+ /**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
* service and is responsible for adding/removing the service in the data structures
@@ -631,6 +714,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
int mId = 0;
+ ServiceInfo mServiceInfo;
+
IBinder mService;
IEventListener mServiceInterface;
@@ -655,9 +740,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final SparseArray<AccessibilityEvent> mPendingEvents =
new SparseArray<AccessibilityEvent>();
- Service(ComponentName componentName) {
+ Service(ComponentName componentName, ServiceInfo serviceInfo) {
mId = sIdCounter++;
mComponentName = componentName;
+ mServiceInfo = serviceInfo;
mIntent = new Intent().setComponent(mComponentName);
mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.accessibility_binding_label);
@@ -689,6 +775,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext.unbindService(this);
mComponentNameToServiceMap.remove(mComponentName);
mServices.remove(this);
+ updateStateOnDisabledService(this);
return true;
}
return false;
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
new file mode 100644
index 0000000..4ba6060
--- /dev/null
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -0,0 +1,1540 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
+
+import com.android.server.accessibility.AccessibilityInputFilter.Explorer;
+import com.android.server.wm.InputFilter;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.WindowManagerPolicy;
+import android.view.MotionEvent.PointerCoords;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.Arrays;
+
+/**
+ * This class is a strategy for performing touch exploration. It
+ * transforms the motion event stream by modifying, adding, replacing,
+ * and consuming certain events. The interaction model is:
+ *
+ * <ol>
+ * <li>1. One finger moving around performs touch exploration.</li>
+ * <li>2. Two close fingers moving in the same direction perform a drag.</li>
+ * <li>3. Multi-finger gestures are delivered to view hierarchy.</li>
+ * <li>4. Pointers that have not moved more than a specified distance after they
+ * went down are considered inactive.</li>
+ * <li>5. Two fingers moving too far from each other or in different directions
+ * are considered a multi-finger gesture.</li>
+ * <li>6. Tapping on the last touch explored location within given time and
+ * distance slop performs a click.</li>
+ * <li>7. Tapping and holding for a while on the last touch explored location within
+ * given time and distance slop performs a long press.</li>
+ * <ol>
+ *
+ * @hide
+ */
+public class TouchExplorer implements Explorer {
+ private static final boolean DEBUG = false;
+
+ // Tag for logging received events.
+ private static final String LOG_TAG_RECEIVED = "TouchExplorer-RECEIVED";
+ // Tag for logging injected events.
+ private static final String LOG_TAG_INJECTED = "TouchExplorer-INJECTED";
+ // Tag for logging the current state.
+ private static final String LOG_TAG_STATE = "TouchExplorer-STATE";
+
+ // States this explorer can be in.
+ private static final int STATE_TOUCH_EXPLORING = 0x00000001;
+ private static final int STATE_DRAGGING = 0x00000002;
+ private static final int STATE_DELEGATING = 0x00000004;
+
+ // Human readable symbolic names for the states of the explorer.
+ private static final SparseArray<String> sStateSymbolicNames = new SparseArray<String>();
+ static {
+ SparseArray<String> symbolicNames = sStateSymbolicNames;
+ symbolicNames.append(STATE_TOUCH_EXPLORING, "STATE_TOUCH_EXPLORING");
+ symbolicNames.append(STATE_DRAGGING, "STATE_DRAGING");
+ symbolicNames.append(STATE_DELEGATING, "STATE_DELEGATING");
+ }
+
+ // Invalid pointer ID.
+ private static final int INVALID_POINTER_ID = -1;
+
+ // The coefficient by which to multiply
+ // ViewConfiguration.#getScaledTouchExplorationTapSlop()
+ // to compute #mDraggingDistance.
+ private static final int COEFFICIENT_DRAGGING_DISTANCE = 2;
+
+ // The time slop in milliseconds for activating an item after it has
+ // been touch explored. Tapping on an item within this slop will perform
+ // a click and tapping and holding down a long press.
+ private static final long ACTIVATION_TIME_SLOP = 2000;
+
+ // This constant captures the current implementation detail that
+ // pointer IDs are between 0 and 31 inclusive (subject to change).
+ // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+ private static final int MAX_POINTER_COUNT = 32;
+
+ // The minimum of the cosine between the vectors of two moving
+ // pointers so they can be considered moving in the same direction.
+ private static final float MIN_ANGLE_COS = 0.866025404f; // cos(pi/6)
+
+ // The delay for sending a hover enter event.
+ private static final long DELAY_SEND_HOVER_MOVE = 200;
+
+ // Temporary array for storing pointer IDs.
+ private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
+
+ // Temporary array for mapping new to old pointer IDs while filtering inactive pointers.
+ private final int [] mTempNewToOldPointerIndexMap = new int[MAX_POINTER_COUNT];
+
+ // Temporary array for storing PointerCoords
+ private final PointerCoords[] mTempPointerCoords= new PointerCoords[MAX_POINTER_COUNT];
+
+ // The maximal distance between two pointers so they are
+ // considered to be performing a drag operation.
+ private final float mDraggingDistance;
+
+ // The distance from the last touch explored location tapping within
+ // which would perform a click and tapping and holding a long press.
+ private final int mTouchExplorationTapSlop;
+
+ // Context handle for accessing resources.
+ private final Context mContext;
+
+ // The InputFilter this tracker is associated with i.e. the filter
+ // which delegates event processing to this touch explorer.
+ private final InputFilter mInputFilter;
+
+ // Helper class for tracking pointers on the screen, for example which
+ // pointers are down, which are active, etc.
+ private final PointerTracker mPointerTracker;
+
+ // Handle to the accessibility manager for firing accessibility events
+ // announcing touch exploration gesture start and end.
+ private final AccessibilityManager mAccessibilityManager;
+
+ // The last event that was received while performing touch exploration.
+ private MotionEvent mLastTouchExploreEvent;
+
+ // The current state of the touch explorer.
+ private int mCurrentState = STATE_TOUCH_EXPLORING;
+
+ // Flag whether a touch exploration gesture is in progress.
+ private boolean mTouchExploreGestureInProgress;
+
+ // The ID of the pointer used for dragging.
+ private int mDraggingPointerId;
+
+ // Handler for performing asynchronous operations.
+ private final Handler mHandler;
+
+ // Command for delayed sending of a hover event.
+ private final SendHoverDelayed mSendHoverDelayed;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param inputFilter The input filter associated with this explorer.
+ * @param context A context handle for accessing resources.
+ */
+ public TouchExplorer(InputFilter inputFilter, Context context) {
+ mInputFilter = inputFilter;
+ mTouchExplorationTapSlop =
+ ViewConfiguration.get(context).getScaledTouchExplorationTapSlop();
+ mDraggingDistance = mTouchExplorationTapSlop * COEFFICIENT_DRAGGING_DISTANCE;
+ mPointerTracker = new PointerTracker(context);
+ mContext = context;
+ mHandler = new Handler(context.getMainLooper());
+ mSendHoverDelayed = new SendHoverDelayed();
+ mAccessibilityManager = AccessibilityManager.getInstance(context);
+
+ // Populate the temporary array with PointerCorrds to be reused.
+ for (int i = 0, count = mTempPointerCoords.length; i < count; i++) {
+ mTempPointerCoords[i] = new PointerCoords();
+ }
+ }
+
+ public void clear(MotionEvent event, int policyFlags) {
+ sendUpForInjectedDownPointers(event, policyFlags);
+ clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG_RECEIVED, "Received event: " + event + ", policyFlags=0x"
+ + Integer.toHexString(policyFlags));
+ Slog.d(LOG_TAG_STATE, sStateSymbolicNames.get(mCurrentState));
+ }
+
+ // Keep track of the pointers's state.
+ mPointerTracker.onReceivedMotionEvent(event);
+
+ switch(mCurrentState) {
+ case STATE_TOUCH_EXPLORING: {
+ handleMotionEventStateTouchExploring(event, policyFlags);
+ } break;
+ case STATE_DRAGGING: {
+ handleMotionEventStateDragging(event, policyFlags);
+ } break;
+ case STATE_DELEGATING: {
+ handleMotionEventStateDelegating(event, policyFlags);
+ } break;
+ default: {
+ throw new IllegalStateException("Illegal state: " + mCurrentState);
+ }
+ }
+ }
+
+ /**
+ * Handles a motion event in touch exploring state.
+ *
+ * @param event The event to be handled.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void handleMotionEventStateTouchExploring(MotionEvent event, int policyFlags) {
+ PointerTracker pointerTracker = mPointerTracker;
+ final int activePointerCount = pointerTracker.getActivePointerCount();
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ // Send a hover for every finger down so the user gets feedback
+ // where she is currently touching.
+ mSendHoverDelayed.forceSendAndRemove();
+ mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, 0, policyFlags,
+ DELAY_SEND_HOVER_MOVE);
+ } break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ switch (activePointerCount) {
+ case 0: {
+ throw new IllegalStateException("The must always be one active pointer in"
+ + "touch exploring state!");
+ }
+ case 1: {
+ // Schedule a hover event which will lead to firing an
+ // accessibility event from the hovered view.
+ mSendHoverDelayed.remove();
+ final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerIndex = event.findPointerIndex(pointerId);
+ final int lastAction = pointerTracker.getLastInjectedHoverAction();
+ // If a schedules hover enter for another pointer is delivered we send move.
+ final int action = (lastAction == MotionEvent.ACTION_HOVER_ENTER)
+ ? MotionEvent.ACTION_HOVER_MOVE
+ : MotionEvent.ACTION_HOVER_ENTER;
+ mSendHoverDelayed.post(event, action, pointerIndex, policyFlags,
+ DELAY_SEND_HOVER_MOVE);
+
+ if (mLastTouchExploreEvent == null) {
+ break;
+ }
+
+ // If more pointers down on the screen since the last touch
+ // exploration we discard the last cached touch explore event.
+ if (event.getPointerCount() != mLastTouchExploreEvent.getPointerCount()) {
+ mLastTouchExploreEvent = null;
+ }
+ } break;
+ default: {
+ /* do nothing - let the code for ACTION_MOVE decide what to do */
+ } break;
+ }
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ switch (activePointerCount) {
+ case 0: {
+ /* do nothing - no active pointers so we swallow the event */
+ } break;
+ case 1: {
+ final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerIndex = event.findPointerIndex(pointerId);
+
+ // Detect touch exploration gesture start by having one active pointer
+ // that moved more than a given distance.
+ if (!mTouchExploreGestureInProgress) {
+ final float deltaX = pointerTracker.getReceivedPointerDownX(pointerId)
+ - event.getX(pointerIndex);
+ final float deltaY = pointerTracker.getReceivedPointerDownY(pointerId)
+ - event.getY(pointerIndex);
+ final double moveDelta = Math.hypot(deltaX, deltaY);
+
+ if (moveDelta > mTouchExplorationTapSlop) {
+ mTouchExploreGestureInProgress = true;
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
+ // Make sure the scheduled down/move event is sent.
+ mSendHoverDelayed.forceSendAndRemove();
+ sendHoverEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIndex,
+ policyFlags);
+ }
+ } else {
+ // Touch exploration gesture in progress so send a hover event.
+ sendHoverEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIndex,
+ policyFlags);
+ }
+
+ // Detect long press on the last touch explored position.
+ if (!mTouchExploreGestureInProgress && mLastTouchExploreEvent != null) {
+
+ // If the down was not in the time slop => nothing else to do.
+ final long pointerDownTime =
+ pointerTracker.getReceivedPointerDownTime(pointerId);
+ final long lastExploreTime = mLastTouchExploreEvent.getEventTime();
+ final long deltaTimeExplore = pointerDownTime - lastExploreTime;
+ if (deltaTimeExplore > ACTIVATION_TIME_SLOP) {
+ mLastTouchExploreEvent = null;
+ break;
+ }
+
+ // If the pointer moved more than the tap slop => nothing else to do.
+ final float deltaX = mLastTouchExploreEvent.getX(pointerIndex)
+ - event.getX(pointerIndex);
+ final float deltaY = mLastTouchExploreEvent.getY(pointerIndex)
+ - event.getY(pointerIndex);
+ final float moveDelta = (float) Math.hypot(deltaX, deltaY);
+ if (moveDelta > mTouchExplorationTapSlop) {
+ mLastTouchExploreEvent = null;
+ break;
+ }
+
+ // If down for long enough we get a long press.
+ final long deltaTimeMove = event.getEventTime() - pointerDownTime;
+ if (deltaTimeMove > ViewConfiguration.getLongPressTimeout()) {
+ mCurrentState = STATE_DELEGATING;
+ // Make sure the scheduled hover exit is delivered.
+ mSendHoverDelayed.forceSendAndRemove();
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ sendMotionEvent(event, policyFlags);
+ mTouchExploreGestureInProgress = false;
+ mLastTouchExploreEvent = null;
+ }
+ }
+ } break;
+ case 2: {
+ // Make sure the scheduled hover enter is delivered.
+ mSendHoverDelayed.forceSendAndRemove();
+ // We want to no longer hover over the location so subsequent
+ // touch at the same spot will generate a hover enter.
+ final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerIndex = event.findPointerIndex(pointerId);
+ sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex,
+ policyFlags);
+
+ if (isDraggingGesture(event)) {
+ // Two pointers moving in the same direction within
+ // a given distance perform a drag.
+ mCurrentState = STATE_DRAGGING;
+ if (mTouchExploreGestureInProgress) {
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mTouchExploreGestureInProgress = false;
+ }
+ mDraggingPointerId = pointerTracker.getPrimaryActivePointerId();
+ sendDragEvent(event, MotionEvent.ACTION_DOWN, policyFlags);
+ } else {
+ // Two pointers moving arbitrary are delegated to the view hierarchy.
+ mCurrentState = STATE_DELEGATING;
+ if (mTouchExploreGestureInProgress) {
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mTouchExploreGestureInProgress = false;
+ }
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ } break;
+ default: {
+ // Make sure the scheduled hover enter is delivered.
+ mSendHoverDelayed.forceSendAndRemove();
+ // We want to no longer hover over the location so subsequent
+ // touch at the same spot will generate a hover enter.
+ final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerIndex = event.findPointerIndex(pointerId);
+ sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex,
+ policyFlags);
+
+ // More than two pointers are delegated to the view hierarchy.
+ mCurrentState = STATE_DELEGATING;
+ mSendHoverDelayed.remove();
+ if (mTouchExploreGestureInProgress) {
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mTouchExploreGestureInProgress = false;
+ }
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ }
+ } break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP: {
+ switch (activePointerCount) {
+ case 0: {
+ // If the pointer that went up was not active we have nothing to do.
+ if (!pointerTracker.wasLastReceivedUpPointerActive()) {
+ break;
+ }
+
+ // If touch exploring announce the end of the gesture.
+ if (mTouchExploreGestureInProgress) {
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mTouchExploreGestureInProgress = false;
+ }
+
+ // Detect whether to activate i.e. click on the last explored location.
+ if (mLastTouchExploreEvent != null) {
+ final int pointerId = pointerTracker.getLastReceivedUpPointerId();
+
+ // If the down was not in the time slop => nothing else to do.
+ final long eventTime =
+ pointerTracker.getLastReceivedUpPointerDownTime();
+ final long exploreTime = mLastTouchExploreEvent.getEventTime();
+ final long deltaTime = eventTime - exploreTime;
+ if (deltaTime > ACTIVATION_TIME_SLOP) {
+ mSendHoverDelayed.forceSendAndRemove();
+ scheduleHoverExit(event, policyFlags);
+ mLastTouchExploreEvent = MotionEvent.obtain(event);
+ break;
+ }
+
+ // If the pointer moved more than the tap slop => nothing else to do.
+ final int pointerIndex = event.findPointerIndex(pointerId);
+ final float deltaX = pointerTracker.getLastReceivedUpPointerDownX()
+ - event.getX(pointerIndex);
+ final float deltaY = pointerTracker.getLastReceivedUpPointerDownY()
+ - event.getY(pointerIndex);
+ final float deltaMove = (float) Math.hypot(deltaX, deltaY);
+ if (deltaMove > mTouchExplorationTapSlop) {
+ mSendHoverDelayed.forceSendAndRemove();
+ scheduleHoverExit(event, policyFlags);
+ mLastTouchExploreEvent = MotionEvent.obtain(event);
+ break;
+ }
+
+ // All preconditions are met, so click the last explored location.
+ mSendHoverDelayed.forceSendAndRemove();
+ sendActionDownAndUp(mLastTouchExploreEvent, policyFlags);
+ mLastTouchExploreEvent = null;
+ } else {
+ mSendHoverDelayed.forceSendAndRemove();
+ scheduleHoverExit(event, policyFlags);
+ mLastTouchExploreEvent = MotionEvent.obtain(event);
+ }
+ } break;
+ }
+ } break;
+ case MotionEvent.ACTION_CANCEL: {
+ final int lastAction = pointerTracker.getLastInjectedHoverAction();
+ if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
+ final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerIndex = event.findPointerIndex(pointerId);
+ sendHoverEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIndex,
+ policyFlags);
+ }
+ clear();
+ } break;
+ }
+ }
+
+ /**
+ * Handles a motion event in dragging state.
+ *
+ * @param event The event to be handled.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ throw new IllegalStateException("Dragging state can be reached only if two "
+ + "pointers are already down");
+ }
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ // We are in dragging state so we have two pointers and another one
+ // goes down => delegate the three pointers to the view hierarchy
+ mCurrentState = STATE_DELEGATING;
+ sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags);
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ final int activePointerCount = mPointerTracker.getActivePointerCount();
+ switch (activePointerCount) {
+ case 2: {
+ if (isDraggingGesture(event)) {
+ // If still dragging send a drag event.
+ sendDragEvent(event, MotionEvent.ACTION_MOVE, policyFlags);
+ } else {
+ // The two pointers are moving either in different directions or
+ // no close enough => delegate the gesture to the view hierarchy.
+ mCurrentState = STATE_DELEGATING;
+ // Send an event to the end of the drag gesture.
+ sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags);
+ // Deliver all active pointers to the view hierarchy.
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ } break;
+ default: {
+ mCurrentState = STATE_DELEGATING;
+ // Send an event to the end of the drag gesture.
+ sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags);
+ // Deliver all active pointers to the view hierarchy.
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ }
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ // Send an event to the end of the drag gesture.
+ sendDragEvent(event, MotionEvent.ACTION_UP, policyFlags);
+ } break;
+ case MotionEvent.ACTION_CANCEL: {
+ clear();
+ } break;
+ }
+ }
+
+ /**
+ * Handles a motion event in delegating state.
+ *
+ * @param event The event to be handled.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ public void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ throw new IllegalStateException("Delegating state can only be reached if "
+ + "there is at least one pointer down!");
+ }
+ case MotionEvent.ACTION_UP: {
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ // Check whether some other pointer became active because they have moved
+ // a given distance and if such exist send them to the view hierarchy
+ final int notInjectedCount = mPointerTracker.getNotInjectedActivePointerCount();
+ if (notInjectedCount > 0) {
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ // No active pointers => go to initial state.
+ if (mPointerTracker.getActivePointerCount() == 0) {
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ }
+ } break;
+ case MotionEvent.ACTION_CANCEL: {
+ clear();
+ } break;
+ }
+ // Deliver the event striping out inactive pointers.
+ sendMotionEventStripInactivePointers(event, policyFlags);
+ }
+
+ /**
+ * Schedules a hover up event so subsequent poking on the same location after
+ * the scheduled delay will perform exploration.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void scheduleHoverExit(MotionEvent prototype,
+ int policyFlags) {
+ final int pointerId = mPointerTracker.getLastReceivedUpPointerId();
+ final int pointerIndex = prototype.findPointerIndex(pointerId);
+ // We want to no longer hover over the location so subsequent
+ // touch at the same spot will generate a hover enter.
+ mSendHoverDelayed.post(prototype, MotionEvent.ACTION_HOVER_EXIT,
+ pointerIndex, policyFlags, ACTIVATION_TIME_SLOP);
+ }
+
+ /**
+ * Sends down events to the view hierarchy for all active pointers which are
+ * not already being delivered i.e. pointers that are not yet injected.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendDownForAllActiveNotInjectedPointers(MotionEvent prototype, int policyFlags) {
+ PointerCoords[] pointerCoords = mTempPointerCoords;
+ PointerTracker pointerTracker = mPointerTracker;
+ int[] pointerIds = mTempPointerIds;
+ int pointerDataIndex = 0;
+
+ final int pinterCount = prototype.getPointerCount();
+ for (int i = 0; i < pinterCount; i++) {
+ final int pointerId = prototype.getPointerId(i);
+
+ // Skip inactive pointers.
+ if (!pointerTracker.isActivePointer(pointerId)) {
+ continue;
+ }
+ // Skip already delivered pointers.
+ if (pointerTracker.isInjectedPointerDown(pointerId)) {
+ continue;
+ }
+
+ // Populate and inject an event for the current pointer.
+ pointerIds[pointerDataIndex] = pointerId;
+ prototype.getPointerCoords(i, pointerCoords[pointerDataIndex]);
+
+ final long downTime = pointerTracker.getLastInjectedDownEventTime();
+ final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, pointerDataIndex);
+ final int pointerCount = pointerDataIndex + 1;
+ final long pointerDownTime = SystemClock.uptimeMillis();
+
+ MotionEvent event = MotionEvent.obtain(downTime, pointerDownTime,
+ action, pointerCount, pointerIds, pointerCoords, prototype.getMetaState(),
+ prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+ prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+ sendMotionEvent(event, policyFlags);
+ event.recycle();
+
+ pointerDataIndex++;
+ }
+ }
+
+ /**
+ * Sends up events to the view hierarchy for all active pointers which are
+ * already being delivered i.e. pointers that are injected.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
+ PointerTracker pointerTracker = mPointerTracker;
+ PointerCoords[] pointerCoords = mTempPointerCoords;
+ int[] pointerIds = mTempPointerIds;
+ int pointerDataIndex = 0;
+
+ final int pointerCount = prototype.getPointerCount();
+ for (int i = 0; i < pointerCount; i++) {
+ final int pointerId = prototype.getPointerId(i);
+
+ // Skip non injected down pointers.
+ if (!pointerTracker.isInjectedPointerDown(pointerId)) {
+ continue;
+ }
+
+ // Populate and inject event.
+ pointerIds[pointerDataIndex] = pointerId;
+ prototype.getPointerCoords(i, pointerCoords[pointerDataIndex]);
+
+ final long downTime = pointerTracker.getLastInjectedDownEventTime();
+ final int action = computeInjectionAction(MotionEvent.ACTION_UP, pointerDataIndex);
+ final int newPointerCount = pointerDataIndex + 1;
+ final long eventTime = SystemClock.uptimeMillis();
+
+ MotionEvent event = MotionEvent.obtain(downTime, eventTime, action,
+ newPointerCount, pointerIds, pointerCoords, prototype.getMetaState(),
+ prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+ prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+
+ sendMotionEvent(event, policyFlags);
+ event.recycle();
+
+ pointerDataIndex++;
+ }
+ }
+
+ /**
+ * Sends a motion event by first stripping the inactive pointers.
+ *
+ * @param prototype The prototype from which to create the injected event.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendMotionEventStripInactivePointers(MotionEvent prototype, int policyFlags) {
+ PointerTracker pointerTracker = mPointerTracker;
+
+ // All pointers active therefore we just inject the event as is.
+ if (prototype.getPointerCount() == pointerTracker.getActivePointerCount()) {
+ sendMotionEvent(prototype, policyFlags);
+ return;
+ }
+
+ // No active pointers and the one that just went up was not
+ // active, therefore we have nothing to do.
+ if (pointerTracker.getActivePointerCount() == 0
+ && !pointerTracker.wasLastReceivedUpPointerActive()) {
+ return;
+ }
+
+ // Filter out inactive pointers from the event and inject it.
+ PointerCoords[] pointerCoords = mTempPointerCoords;
+ int[] pointerIds = mTempPointerIds;
+ int [] newToOldPointerIndexMap = mTempNewToOldPointerIndexMap;
+ int newPointerIndex = 0;
+ int actionIndex = prototype.getActionIndex();
+
+ final int oldPointerCount = prototype.getPointerCount();
+ for (int oldPointerIndex = 0; oldPointerIndex < oldPointerCount; oldPointerIndex++) {
+ final int pointerId = prototype.getPointerId(oldPointerIndex);
+
+ // If the pointer is inactive or the pointer that just went up
+ // was inactive we strip the pointer data from the event.
+ if (!pointerTracker.isActiveOrWasLastActiveUpPointer(pointerId)) {
+ if (oldPointerIndex <= prototype.getActionIndex()) {
+ actionIndex--;
+ }
+ continue;
+ }
+
+ newToOldPointerIndexMap[newPointerIndex] = oldPointerIndex;
+ pointerIds[newPointerIndex] = pointerId;
+ prototype.getPointerCoords(oldPointerIndex, pointerCoords[newPointerIndex]);
+
+ newPointerIndex++;
+ }
+
+ // If we skipped all pointers => nothing to do.
+ if (newPointerIndex == 0) {
+ return;
+ }
+
+ // Populate and inject the event.
+ final long downTime = pointerTracker.getLastInjectedDownEventTime();
+ final int action = computeInjectionAction(prototype.getActionMasked(), actionIndex);
+ final int newPointerCount = newPointerIndex;
+ MotionEvent prunedEvent = MotionEvent.obtain(downTime, prototype.getEventTime(), action,
+ newPointerCount, pointerIds, pointerCoords, prototype.getMetaState(),
+ prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+ prototype.getEdgeFlags(), prototype.getSource(),prototype.getFlags());
+
+ // Add the filtered history.
+ final int historySize = prototype.getHistorySize();
+ for (int historyIndex = 0; historyIndex < historySize; historyIndex++) {
+ for (int pointerIndex = 0; pointerIndex < newPointerCount; pointerIndex++) {
+ final int oldPointerIndex = newToOldPointerIndexMap[pointerIndex];
+ prototype.getPointerCoords(oldPointerIndex, pointerCoords[pointerIndex]);
+ }
+ final long historicalTime = prototype.getHistoricalEventTime(historyIndex);
+ prunedEvent.addBatch(historicalTime, pointerCoords, 0);
+ }
+
+ sendMotionEvent(prunedEvent, policyFlags);
+ prunedEvent.recycle();
+ }
+
+ /**
+ * Sends a dragging event from a two pointer event. The two pointers are
+ * merged into one and delivered to the view hierarchy. Through the entire
+ * drag gesture the pointer id delivered to the view hierarchy is the same.
+ *
+ * @param prototype The prototype from which to create the injected event.
+ * @param action The dragging action that is to be injected.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendDragEvent(MotionEvent prototype, int action, int policyFlags) {
+ PointerCoords[] pointerCoords = mTempPointerCoords;
+ int[] pointerIds = mTempPointerIds;
+ final int pointerId = mDraggingPointerId;
+ final int pointerIndex = prototype.findPointerIndex(pointerId);
+
+ // Populate the event with the date of the dragging pointer and inject it.
+ pointerIds[0] = pointerId;
+ prototype.getPointerCoords(pointerIndex, pointerCoords[0]);
+
+ MotionEvent event = MotionEvent.obtain(prototype.getDownTime(),
+ prototype.getEventTime(), action, 1, pointerIds, pointerCoords,
+ prototype.getMetaState(), prototype.getXPrecision(), prototype.getYPrecision(),
+ prototype.getDeviceId(), prototype.getEdgeFlags(), prototype.getSource(),
+ prototype.getFlags());
+
+ sendMotionEvent(event, policyFlags);
+ event.recycle();
+ }
+
+ /**
+ * Sends an up and down events.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) {
+ PointerCoords[] pointerCoords = mTempPointerCoords;
+ int[] pointerIds = mTempPointerIds;
+ final int pointerId = mPointerTracker.getLastReceivedUpPointerId();
+ final int pointerIndex = prototype.findPointerIndex(pointerId);
+
+ // Send down.
+ pointerIds[0] = pointerId;
+ prototype.getPointerCoords(pointerIndex, pointerCoords[0]);
+
+ final long downTime = SystemClock.uptimeMillis();
+
+ MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+ 1, mTempPointerIds, mTempPointerCoords, prototype.getMetaState(),
+ prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+ prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+
+ // Clone the down event before recycling it.
+ MotionEvent upEvent = MotionEvent.obtain(downEvent);
+
+ sendMotionEvent(downEvent, policyFlags);
+ downEvent.recycle();
+
+ // Send up.
+ upEvent.setAction(MotionEvent.ACTION_UP);
+ sendMotionEvent(upEvent, policyFlags);
+ upEvent.recycle();
+ }
+
+ /**
+ * Sends a hover event.
+ *
+ * @param prototype The prototype from which to create the injected event.
+ * @param action The hover action.
+ * @param pointerIndex The action pointer index.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendHoverEvent(MotionEvent prototype, int action, int pointerIndex, int
+ policyFlags) {
+ PointerCoords[] pointerCoords = mTempPointerCoords;
+ int[] pointerIds = mTempPointerIds;
+
+ // Keep only data relevant to a hover event.
+ pointerIds[0] = prototype.getPointerId(pointerIndex);
+ pointerCoords[0].clear();
+ pointerCoords[0].x = prototype.getX(pointerIndex);
+ pointerCoords[0].y = prototype.getY(pointerIndex);
+
+ final long downTime = mPointerTracker.getLastInjectedDownEventTime();
+
+ // Populate and inject a hover event.
+ MotionEvent hoverEvent = MotionEvent.obtain(downTime, prototype.getEventTime(), action,
+ 1, pointerIds, pointerCoords, 0, 0, 0, prototype.getDeviceId(), 0,
+ prototype.getSource(), 0);
+
+ sendMotionEvent(hoverEvent, policyFlags);
+ hoverEvent.recycle();
+ }
+
+ /**
+ * Computes the action for an injected event based on a masked action
+ * and a pointer index.
+ *
+ * @param actionMasked The masked action.
+ * @param pointerIndex The index of the pointer which has changed.
+ * @return The action to be used for injection.
+ */
+ private int computeInjectionAction(int actionMasked, int pointerIndex) {
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ PointerTracker pointerTracker = mPointerTracker;
+ // Compute the action based on how many down pointers are injected.
+ if (pointerTracker.getInjectedPointerDownCount() == 0) {
+ return MotionEvent.ACTION_DOWN;
+ } else {
+ return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ | MotionEvent.ACTION_POINTER_DOWN;
+ }
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ PointerTracker pointerTracker = mPointerTracker;
+ // Compute the action based on how many down pointers are injected.
+ if (pointerTracker.getInjectedPointerDownCount() == 1) {
+ return MotionEvent.ACTION_UP;
+ } else {
+ return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ | MotionEvent.ACTION_POINTER_UP;
+ }
+ }
+ default:
+ return actionMasked;
+ }
+ }
+
+ /**
+ * Determines whether a two pointer gesture is a dragging one.
+ *
+ * @param event The event with the pointer data.
+ * @return True if the gesture is a dragging one.
+ */
+ private boolean isDraggingGesture(MotionEvent event) {
+ PointerTracker pointerTracker = mPointerTracker;
+ int[] pointerIds = mTempPointerIds;
+ pointerTracker.populateActivePointerIds(pointerIds);
+
+ final int firstPtrIndex = event.findPointerIndex(pointerIds[0]);
+ final int secondPtrIndex = event.findPointerIndex(pointerIds[1]);
+
+ final float firstPtrX = event.getX(firstPtrIndex);
+ final float firstPtrY = event.getY(firstPtrIndex);
+ final float secondPtrX = event.getX(secondPtrIndex);
+ final float secondPtrY = event.getY(secondPtrIndex);
+
+ // Check if the pointers are close enough.
+ final float deltaX = firstPtrX - secondPtrX;
+ final float deltaY = firstPtrY - secondPtrY;
+ final float deltaMove = (float) Math.hypot(deltaX, deltaY);
+ if (deltaMove > mDraggingDistance) {
+ return false;
+ }
+
+ // Check if the pointers are moving in the same direction.
+ final float firstDeltaX =
+ firstPtrX - pointerTracker.getReceivedPointerDownX(firstPtrIndex);
+ final float firstDeltaY =
+ firstPtrY - pointerTracker.getReceivedPointerDownY(firstPtrIndex);
+ final float firstMagnitude =
+ (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY);
+ final float firstXNormalized =
+ (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
+ final float firstYNormalized =
+ (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
+
+ final float secondDeltaX =
+ secondPtrX - pointerTracker.getReceivedPointerDownX(secondPtrIndex);
+ final float secondDeltaY =
+ secondPtrY - pointerTracker.getReceivedPointerDownY(secondPtrIndex);
+ final float secondMagnitude =
+ (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY);
+ final float secondXNormalized =
+ (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
+ final float secondYNormalized =
+ (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
+
+ final float angleCos =
+ firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
+
+ if (angleCos < MIN_ANGLE_COS) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Sends an event announcing the start/end of a touch exploration gesture.
+ *
+ * @param eventType The type of the event to send.
+ */
+ private void sendAccessibilityEvent(int eventType) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ event.setPackageName(mContext.getPackageName());
+ event.setClassName(getClass().getName());
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
+ /**
+ * Sends a motion event to the input filter for injection.
+ *
+ * @param event The event to send.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendMotionEvent(MotionEvent event, int policyFlags) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG_INJECTED, "Injecting event: " + event + ", policyFlags=0x"
+ + Integer.toHexString(policyFlags));
+ }
+ // Make sure that the user will see the event.
+ policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
+ mPointerTracker.onInjectedMotionEvent(event);
+ mInputFilter.sendInputEvent(event, policyFlags);
+ }
+
+ /**
+ * Clears the internal state of this explorer.
+ */
+ private void clear() {
+ mSendHoverDelayed.remove();
+ mPointerTracker.clear();
+ mLastTouchExploreEvent = null;
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ mTouchExploreGestureInProgress = false;
+ mDraggingPointerId = INVALID_POINTER_ID;
+ }
+
+ /**
+ * Helper class for tracking pointers and more specifically which of
+ * them are currently down, which are active, and which are delivered
+ * to the view hierarchy. The enclosing {@link TouchExplorer} uses the
+ * pointer state reported by this class to perform touch exploration.
+ * <p>
+ * The main purpose of this class is to allow the touch explorer to
+ * disregard pointers put down by accident by the user and not being
+ * involved in the interaction. For example, a blind user grabs the
+ * device with her left hand such that she touches the screen and she
+ * uses her right hand's index finger to explore the screen content.
+ * In this scenario the touches generated by the left hand are to be
+ * ignored.
+ */
+ class PointerTracker {
+ private static final String LOG_TAG = "PointerTracker";
+
+ // The coefficient by which to multiply
+ // ViewConfiguration.#getScaledTouchSlop()
+ // to compute #mThresholdActivePointer.
+ private static final int COEFFICIENT_ACTIVE_POINTER = 2;
+
+ // Pointers that moved less than mThresholdActivePointer
+ // are considered active i.e. are ignored.
+ private final double mThresholdActivePointer;
+
+ // Keep track of where and when a pointer went down.
+ private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT];
+ private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT];
+ private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT];
+
+ // Which pointers are down.
+ private int mReceivedPointersDown;
+
+ // Which down pointers are active.
+ private int mActivePointers;
+
+ // Primary active pointer which is either the first that went down
+ // or if it goes up the next active that most recently went down.
+ private int mPrimaryActivePointerId;
+
+ // Flag indicating that there is at least one active pointer moving.
+ private boolean mHasMovingActivePointer;
+
+ // Keep track of which pointers sent to the system are down.
+ private int mInjectedPointersDown;
+
+ // Keep track of the last up pointer data.
+ private float mLastReceivedUpPointerDownX;
+ private float mLastReveivedUpPointerDownY;
+ private long mLastReceivedUpPointerDownTime;
+ private int mLastReceivedUpPointerId;
+ private boolean mLastReceivedUpPointerActive;
+
+ // The time of the last injected down.
+ private long mLastInjectedDownEventTime;
+
+ // The action of the last injected hover event.
+ private int mLastInjectedHoverEventAction = MotionEvent.ACTION_HOVER_EXIT;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for looking up resources.
+ */
+ public PointerTracker(Context context) {
+ mThresholdActivePointer =
+ ViewConfiguration.get(context).getScaledTouchSlop() * COEFFICIENT_ACTIVE_POINTER;
+ }
+
+ /**
+ * Clears the internals state.
+ */
+ public void clear() {
+ Arrays.fill(mReceivedPointerDownX, 0);
+ Arrays.fill(mReceivedPointerDownY, 0);
+ Arrays.fill(mReceivedPointerDownTime, 0);
+ mReceivedPointersDown = 0;
+ mActivePointers = 0;
+ mPrimaryActivePointerId = 0;
+ mHasMovingActivePointer = false;
+ mInjectedPointersDown = 0;
+ mLastReceivedUpPointerDownX = 0;
+ mLastReveivedUpPointerDownY = 0;
+ mLastReceivedUpPointerDownTime = 0;
+ mLastReceivedUpPointerId = 0;
+ mLastReceivedUpPointerActive = false;
+ }
+
+ /**
+ * Processes a received {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ public void onReceivedMotionEvent(MotionEvent event) {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ // New gesture so restart tracking injected down pointers.
+ mInjectedPointersDown = 0;
+ handleReceivedPointerDown(0, event);
+ } break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ handleReceivedPointerDown(event.getActionIndex(), event);
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ handleReceivedPointerMove(event);
+ } break;
+ case MotionEvent.ACTION_UP: {
+ handleReceivedPointerUp(0, event);
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ handleReceivedPointerUp(event.getActionIndex(), event);
+ } break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Received pointer: " + toString());
+ }
+ }
+
+ /**
+ * Processes an injected {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ public void onInjectedMotionEvent(MotionEvent event) {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ handleInjectedPointerDown(0, event);
+ } break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ handleInjectedPointerDown(event.getActionIndex(), event);
+ } break;
+ case MotionEvent.ACTION_UP: {
+ handleInjectedPointerUp(0, event);
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ handleInjectedPointerUp(event.getActionIndex(), event);
+ } break;
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ mLastInjectedHoverEventAction = event.getActionMasked();
+ } break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injected pointer: " + toString());
+ }
+ }
+
+ /**
+ * @return The number of received pointers that are down.
+ */
+ public int getReceivedPointerDownCount() {
+ return Integer.bitCount(mReceivedPointersDown);
+ }
+
+ /**
+ * @return The number of down input pointers that are active.
+ */
+ public int getActivePointerCount() {
+ return Integer.bitCount(mActivePointers);
+ }
+
+ /**
+ * Whether an received pointer is down.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is down.
+ */
+ public boolean isReceivedPointerDown(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mReceivedPointersDown & pointerFlag) != 0;
+ }
+
+ /**
+ * Whether an injected pointer is down.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is down.
+ */
+ public boolean isInjectedPointerDown(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mInjectedPointersDown & pointerFlag) != 0;
+ }
+
+ /**
+ * @return The number of down pointers injected to the view hierarchy.
+ */
+ public int getInjectedPointerDownCount() {
+ return Integer.bitCount(mInjectedPointersDown);
+ }
+
+ /**
+ * Whether an input pointer is active.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is active.
+ */
+ public boolean isActivePointer(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mActivePointers & pointerFlag) != 0;
+ }
+
+ /**
+ * @param pointerId The unique pointer id.
+ * @return The X coordinate where the pointer went down.
+ */
+ public float getReceivedPointerDownX(int pointerId) {
+ return mReceivedPointerDownX[pointerId];
+ }
+
+ /**
+ * @param pointerId The unique pointer id.
+ * @return The Y coordinate where the pointer went down.
+ */
+ public float getReceivedPointerDownY(int pointerId) {
+ return mReceivedPointerDownY[pointerId];
+ }
+
+ /**
+ * @param pointerId The unique pointer id.
+ * @return The time when the pointer went down.
+ */
+ public long getReceivedPointerDownTime(int pointerId) {
+ return mReceivedPointerDownTime[pointerId];
+ }
+
+ /**
+ * @return The id of the primary pointer.
+ */
+ public int getPrimaryActivePointerId() {
+ if (mPrimaryActivePointerId == INVALID_POINTER_ID) {
+ mPrimaryActivePointerId = findPrimaryActivePointer();
+ }
+ return mPrimaryActivePointerId;
+ }
+
+ /**
+ * @return The X coordinate where the last up received pointer went down.
+ */
+ public float getLastReceivedUpPointerDownX() {
+ return mLastReceivedUpPointerDownX;
+ }
+
+ /**
+ * @return The Y coordinate where the last up received pointer went down.
+ */
+ public float getLastReceivedUpPointerDownY() {
+ return mLastReveivedUpPointerDownY;
+ }
+
+ /**
+ * @return The time when the last up received pointer went down.
+ */
+ public long getLastReceivedUpPointerDownTime() {
+ return mLastReceivedUpPointerDownTime;
+ }
+
+ /**
+ * @return The id of the last received pointer that went up.
+ */
+ public int getLastReceivedUpPointerId() {
+ return mLastReceivedUpPointerId;
+ }
+
+ /**
+ * @return Whether the last received pointer that went up was active.
+ */
+ public boolean wasLastReceivedUpPointerActive() {
+ return mLastReceivedUpPointerActive;
+ }
+
+ /**
+ * @return The time of the last injected down event.
+ */
+ public long getLastInjectedDownEventTime() {
+ return mLastInjectedDownEventTime;
+ }
+
+ /**
+ * @return The action of the last injected hover event.
+ */
+ public int getLastInjectedHoverAction() {
+ return mLastInjectedHoverEventAction;
+ }
+
+ /**
+ * Populates the active pointer IDs to the given array.
+ * <p>
+ * Note: The client is responsible for providing large enough array.
+ *
+ * @param outPointerIds The array to which to write the active pointers.
+ */
+ public void populateActivePointerIds(int[] outPointerIds) {
+ int index = 0;
+ for (int idBits = mActivePointers; idBits != 0; ) {
+ final int id = Integer.numberOfTrailingZeros(idBits);
+ idBits &= ~(1 << id);
+ outPointerIds[index] = id;
+ index++;
+ }
+ }
+
+ /**
+ * @return The number of non injected active pointers.
+ */
+ public int getNotInjectedActivePointerCount() {
+ final int pointerState = mActivePointers & ~mInjectedPointersDown;
+ return Integer.bitCount(pointerState);
+ }
+
+ /**
+ * @param pointerId The unique pointer id.
+ * @return Whether the pointer is active or was the last active than went up.
+ */
+ private boolean isActiveOrWasLastActiveUpPointer(int pointerId) {
+ return (isActivePointer(pointerId)
+ || (mLastReceivedUpPointerId == pointerId
+ && mLastReceivedUpPointerActive));
+ }
+
+ /**
+ * Handles a received pointer down event.
+ *
+ * @param pointerIndex The index of the pointer that has changed.
+ * @param event The event to be handled.
+ */
+ private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int pointerFlag = (1 << pointerId);
+
+ mLastReceivedUpPointerId = 0;
+ mLastReceivedUpPointerDownX = 0;
+ mLastReveivedUpPointerDownY = 0;
+ mLastReceivedUpPointerDownTime = 0;
+ mLastReceivedUpPointerActive = false;
+
+ mReceivedPointersDown |= pointerFlag;
+ mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
+ mReceivedPointerDownY[pointerId] = event.getY(pointerIndex);
+ mReceivedPointerDownTime[pointerId] = event.getEventTime();
+
+ if (!mHasMovingActivePointer) {
+ // If still no moving active pointers every
+ // down pointer is the only active one.
+ mActivePointers = pointerFlag;
+ mPrimaryActivePointerId = pointerId;
+ } else {
+ // If at least one moving active pointer every
+ // subsequent down pointer is active.
+ mActivePointers |= pointerFlag;
+ }
+ }
+
+ /**
+ * Handles a received pointer move event.
+ *
+ * @param event The event to be handled.
+ */
+ private void handleReceivedPointerMove(MotionEvent event) {
+ detectActivePointers(event);
+ }
+
+ /**
+ * Handles a received pointer up event.
+ *
+ * @param pointerIndex The index of the pointer that has changed.
+ * @param event The event to be handled.
+ */
+ private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int pointerFlag = (1 << pointerId);
+
+ mLastReceivedUpPointerId = pointerId;
+ mLastReceivedUpPointerDownX = getReceivedPointerDownX(pointerId);
+ mLastReveivedUpPointerDownY = getReceivedPointerDownY(pointerId);
+ mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
+ mLastReceivedUpPointerActive = isActivePointer(pointerId);
+
+ mReceivedPointersDown &= ~pointerFlag;
+ mActivePointers &= ~pointerFlag;
+ mReceivedPointerDownX[pointerId] = 0;
+ mReceivedPointerDownY[pointerId] = 0;
+ mReceivedPointerDownTime[pointerId] = 0;
+
+ if (mActivePointers == 0) {
+ mHasMovingActivePointer = false;
+ }
+ if (mPrimaryActivePointerId == pointerId) {
+ mPrimaryActivePointerId = INVALID_POINTER_ID;
+ }
+ }
+
+ /**
+ * Handles a injected pointer down event.
+ *
+ * @param pointerIndex The index of the pointer that has changed.
+ * @param event The event to be handled.
+ */
+ private void handleInjectedPointerDown(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int pointerFlag = (1 << pointerId);
+ mInjectedPointersDown |= pointerFlag;
+ mLastInjectedDownEventTime = event.getEventTime();
+ }
+
+ /**
+ * Handles a injected pointer up event.
+ *
+ * @param pointerIndex The index of the pointer that has changed.
+ * @param event The event to be handled.
+ */
+ private void handleInjectedPointerUp(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int pointerFlag = (1 << pointerId);
+ mInjectedPointersDown &= ~pointerFlag;
+ }
+
+ /**
+ * Detects the active pointers in an event.
+ *
+ * @param event The event to examine.
+ */
+ private void detectActivePointers(MotionEvent event) {
+ for (int i = 0, count = event.getPointerCount(); i < count; i++) {
+ final int pointerId = event.getPointerId(i);
+ if (mHasMovingActivePointer) {
+ // If already active => nothing to do.
+ if (isActivePointer(pointerId)) {
+ continue;
+ }
+ }
+ // Active pointers are ones that moved more than a given threshold.
+ final float pointerDeltaMove = computePointerDeltaMove(i, event);
+ if (pointerDeltaMove > mThresholdActivePointer) {
+ final int pointerFlag = (1 << pointerId);
+ mActivePointers |= pointerFlag;
+ mHasMovingActivePointer = true;
+ }
+ }
+ }
+
+ /**
+ * @return The primary active pointer.
+ */
+ private int findPrimaryActivePointer() {
+ int primaryActivePointerId = INVALID_POINTER_ID;
+ long minDownTime = Long.MAX_VALUE;
+ // Find the active pointer that went down first.
+ for (int i = 0, count = mReceivedPointerDownTime.length; i < count; i++) {
+ if (isActivePointer(i)) {
+ final long downPointerTime = mReceivedPointerDownTime[i];
+ if (downPointerTime < minDownTime) {
+ minDownTime = downPointerTime;
+ primaryActivePointerId = i;
+ }
+ }
+ }
+ return primaryActivePointerId;
+ }
+
+ /**
+ * Computes the move for a given action pointer index since the
+ * corresponding pointer went down.
+ *
+ * @param pointerIndex The action pointer index.
+ * @param event The event to examine.
+ * @return The distance the pointer has moved.
+ */
+ private float computePointerDeltaMove(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final float deltaX = event.getX(pointerIndex) - mReceivedPointerDownX[pointerId];
+ final float deltaY = event.getY(pointerIndex) - mReceivedPointerDownY[pointerId];
+ return (float) Math.hypot(deltaX, deltaY);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("=========================");
+ builder.append("\nDown pointers #");
+ builder.append(getReceivedPointerDownCount());
+ builder.append(" [ ");
+ for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+ if (isReceivedPointerDown(i)) {
+ builder.append(i);
+ builder.append(" ");
+ }
+ }
+ builder.append("]");
+ builder.append("\nActive pointers #");
+ builder.append(getActivePointerCount());
+ builder.append(" [ ");
+ for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+ if (isActivePointer(i)) {
+ builder.append(i);
+ builder.append(" ");
+ }
+ }
+ builder.append("]");
+ builder.append("\nPrimary active pointer id [ ");
+ builder.append(getPrimaryActivePointerId());
+ builder.append(" ]");
+ builder.append("\n=========================");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Class for delayed sending of hover events.
+ */
+ private final class SendHoverDelayed implements Runnable {
+ private static final String LOG_TAG = "SendHoverEnterOrExitDelayed";
+
+ private MotionEvent mEvent;
+ private int mAction;
+ private int mPointerIndex;
+ private int mPolicyFlags;
+
+ public void post(MotionEvent prototype, int action, int pointerIndex, int policyFlags,
+ long delay) {
+ remove();
+ mEvent = MotionEvent.obtain(prototype);
+ mAction = action;
+ mPointerIndex = pointerIndex;
+ mPolicyFlags = policyFlags;
+ mHandler.postDelayed(this, delay);
+ }
+
+ public void remove() {
+ mHandler.removeCallbacks(this);
+ clear();
+ }
+
+ private boolean isPenidng() {
+ return (mEvent != null);
+ }
+
+ private void clear() {
+ if (!isPenidng()) {
+ return;
+ }
+ mEvent.recycle();
+ mEvent = null;
+ mAction = 0;
+ mPointerIndex = -1;
+ mPolicyFlags = 0;
+ }
+
+ public void forceSendAndRemove() {
+ if (isPenidng()) {
+ run();
+ remove();
+ }
+ }
+
+ public void run() {
+ if (DEBUG) {
+ if (mAction == MotionEvent.ACTION_HOVER_ENTER) {
+ Slog.d(LOG_TAG, "Injecting: " + MotionEvent.ACTION_HOVER_ENTER);
+ } else if (mAction == MotionEvent.ACTION_HOVER_MOVE) {
+ Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_MOVE");
+ } else if (mAction == MotionEvent.ACTION_HOVER_EXIT) {
+ Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_EXIT");
+ }
+ }
+
+ sendHoverEvent(mEvent, mAction, mPointerIndex, mPolicyFlags);
+ clear();
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index edc5bc0..f6ae523 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -44,6 +44,7 @@ import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
+import android.app.IThumbnailRetriever;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
@@ -90,6 +91,7 @@ import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IInterface;
import android.os.IPermissionController;
import android.os.Looper;
import android.os.Message;
@@ -103,7 +105,6 @@ import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.util.Config;
import android.util.EventLog;
import android.util.Slog;
import android.util.Log;
@@ -163,7 +164,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final boolean DEBUG_URI_PERMISSION = localLOGV || false;
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
static final boolean DEBUG_RESULTS = localLOGV || false;
- static final boolean DEBUG_BACKUP = localLOGV || false;
+ static final boolean DEBUG_BACKUP = localLOGV || true;
static final boolean DEBUG_CONFIGURATION = localLOGV || false;
static final boolean DEBUG_POWER = localLOGV || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
@@ -441,8 +442,7 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* List of intents that were used to start the most recent tasks.
*/
- final ArrayList<TaskRecord> mRecentTasks
- = new ArrayList<TaskRecord>();
+ final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
/**
* All of the applications we currently have running organized by name.
@@ -450,8 +450,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
- final ProcessMap<ProcessRecord> mProcessNames
- = new ProcessMap<ProcessRecord>();
+ final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
/**
* The currently running heavy-weight process, if any.
@@ -480,8 +479,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* <p>NOTE: This object is protected by its own lock, NOT the global
* activity manager lock!
*/
- final SparseArray<ProcessRecord> mPidsSelfLocked
- = new SparseArray<ProcessRecord>();
+ final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
/**
* All of the processes that have been forced to be foreground. The key
@@ -1283,6 +1281,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ServiceManager.addService("activity", m);
ServiceManager.addService("meminfo", new MemBinder(m));
+ ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(m));
}
@@ -1404,35 +1403,19 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- ActivityManagerService service = mActivityManagerService;
- ArrayList<ProcessRecord> procs;
- synchronized (mActivityManagerService) {
- if (args != null && args.length > 0
- && args[0].charAt(0) != '-') {
- procs = new ArrayList<ProcessRecord>();
- int pid = -1;
- try {
- pid = Integer.parseInt(args[0]);
- } catch (NumberFormatException e) {
-
- }
- for (int i=service.mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord proc = service.mLruProcesses.get(i);
- if (proc.pid == pid) {
- procs.add(proc);
- } else if (proc.processName.equals(args[0])) {
- procs.add(proc);
- }
- }
- if (procs.size() <= 0) {
- pw.println("No process found for: " + args[0]);
- return;
- }
- } else {
- procs = new ArrayList<ProcessRecord>(service.mLruProcesses);
- }
- }
- dumpApplicationMemoryUsage(fd, pw, procs, " ", args);
+ mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args);
+ }
+ }
+
+ static class GraphicsBinder extends Binder {
+ ActivityManagerService mActivityManagerService;
+ GraphicsBinder(ActivityManagerService activityManagerService) {
+ mActivityManagerService = activityManagerService;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
}
}
@@ -2654,8 +2637,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " finishing=" + r.finishing);
r.makeFinishing();
mMainStack.mHistory.remove(i);
-
- r.inHistory = false;
+ r.takeFromHistory();
mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
mWindowManager.validateAppTokens(mMainStack.mHistory);
@@ -3033,7 +3015,10 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
- Process.killProcess(app.pid);
+ Slog.w(TAG, "Killing " + app + ": background ANR");
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "background ANR");
+ Process.killProcessQuiet(app.pid);
return;
}
@@ -3285,7 +3270,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (callerUid == Process.SYSTEM_UID) {
synchronized (this) {
ProcessRecord app = getProcessRecordLocked(processName, uid);
- if (app != null) {
+ if (app != null && app.thread != null) {
try {
app.thread.scheduleSuicide();
} catch (RemoteException e) {
@@ -3498,7 +3483,9 @@ public final class ActivityManagerService extends ActivityManagerNative
bringDownServiceLocked(sr, true);
}
}
- Process.killProcess(pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL, pid,
+ app.processName, app.setAdj, "start timeout");
+ Process.killProcessQuiet(pid);
if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
Slog.w(TAG, "Unattached app died before backup, skipping");
try {
@@ -3544,7 +3531,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " (IApplicationThread " + thread + "); dropping process");
EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
if (pid > 0 && pid != MY_PID) {
- Process.killProcess(pid);
+ Process.killProcessQuiet(pid);
} else {
try {
thread.scheduleExit();
@@ -3863,16 +3850,7 @@ public final class ActivityManagerService extends ActivityManagerNative
r = (ActivityRecord)mMainStack.mHistory.get(index);
r.icicle = icicle;
r.haveState = true;
- if (thumbnail != null) {
- r.thumbnail = thumbnail;
- if (r.task != null) {
- r.task.lastThumbnail = r.thumbnail;
- }
- }
- r.description = description;
- if (r.task != null) {
- r.task.lastDescription = r.description;
- }
+ r.updateThumbnail(thumbnail, description);
r.stopped = true;
r.state = ActivityState.STOPPED;
if (!r.finishing) {
@@ -4896,16 +4874,10 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
- final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0
- && checkCallingPermission(
- android.Manifest.permission.READ_FRAME_BUFFER)
- == PackageManager.PERMISSION_GRANTED;
-
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;
@@ -4919,7 +4891,6 @@ public final class ActivityManagerService extends ActivityManagerNative
(top.state == ActivityState.INITIALIZING
&& top.task == r.task)) {
top = r;
- topDescription = r.description;
curTask = r.task;
numActivities = numRunning = 0;
}
@@ -4929,9 +4900,6 @@ public final class ActivityManagerService extends ActivityManagerNative
if (r.app != null && r.app.thread != null) {
numRunning++;
}
- if (topDescription == null) {
- topDescription = r.description;
- }
if (localLOGV) Slog.v(
TAG, r.intent.getComponent().flattenToShortString()
@@ -4945,14 +4913,9 @@ public final class ActivityManagerService extends ActivityManagerNative
ci.id = curTask.taskId;
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
- if (canReadFb) {
- if (top.thumbnail != null) {
- ci.thumbnail = top.thumbnail;
- } else if (top.state == ActivityState.RESUMED) {
- ci.thumbnail = top.stack.screenshotActivities(top);
- }
+ if (top.thumbHolder != null) {
+ ci.description = top.thumbHolder.lastDescription;
}
- ci.description = topDescription;
ci.numActivities = numActivities;
ci.numRunning = numRunning;
//System.out.println(
@@ -5022,8 +4985,6 @@ public final class ActivityManagerService extends ActivityManagerNative
IPackageManager pm = AppGlobals.getPackageManager();
- ActivityRecord resumed = mMainStack.mResumedActivity;
-
final int N = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res
= new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -5069,24 +5030,121 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public Bitmap getTaskThumbnail(int id) {
+ private TaskRecord taskForIdLocked(int id) {
+ final int N = mRecentTasks.size();
+ for (int i=0; i<N; i++) {
+ TaskRecord tr = mRecentTasks.get(i);
+ if (tr.taskId == id) {
+ return tr;
+ }
+ }
+ return null;
+ }
+
+ public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
- "getTaskThumbnail()");
- ActivityRecord resumed = mMainStack.mResumedActivity;
- final int N = mRecentTasks.size();
- for (int i=0; i<N; i++) {
- TaskRecord tr = mRecentTasks.get(i);
- if (tr.taskId == id) {
- if (resumed != null && resumed.task == tr) {
- return resumed.stack.screenshotActivities(resumed);
- } else {
- return tr.lastThumbnail;
+ "getTaskThumbnails()");
+ TaskRecord tr = taskForIdLocked(id);
+ if (tr != null) {
+ return mMainStack.getTaskThumbnailsLocked(tr);
+ }
+ }
+ return null;
+ }
+
+ public boolean removeSubTask(int taskId, int subTaskIndex) {
+ synchronized (this) {
+ enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+ "removeSubTask()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mMainStack.removeTaskActivitiesLocked(taskId, subTaskIndex) != null;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private void removeTaskProcessesLocked(ActivityRecord root) {
+ TaskRecord tr = root.task;
+ Intent baseIntent = new Intent(
+ tr.intent != null ? tr.intent : tr.affinityIntent);
+ ComponentName component = baseIntent.getComponent();
+ if (component == null) {
+ Slog.w(TAG, "Now component for base intent of task: " + tr);
+ return;
+ }
+
+ // Find any running services associated with this app.
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ for (ServiceRecord sr : mServices.values()) {
+ if (sr.packageName.equals(component.getPackageName())) {
+ services.add(sr);
+ }
+ }
+
+ // Take care of any running services associated with the app.
+ for (int i=0; i<services.size(); i++) {
+ ServiceRecord sr = services.get(i);
+ if (sr.startRequested) {
+ if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
+ stopServiceLocked(sr);
+ } else {
+ sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
+ sr.makeNextStartId(), baseIntent, -1));
+ if (sr.app != null && sr.app.thread != null) {
+ sendServiceArgsLocked(sr, false);
}
}
}
}
- return null;
+
+ // Find any running processes associated with this app.
+ ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
+ SparseArray<ProcessRecord> appProcs
+ = mProcessNames.getMap().get(component.getPackageName());
+ if (appProcs != null) {
+ for (int i=0; i<appProcs.size(); i++) {
+ procs.add(appProcs.valueAt(i));
+ }
+ }
+
+ // Kill the running processes.
+ for (int i=0; i<procs.size(); i++) {
+ ProcessRecord pr = procs.get(i);
+ if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ Slog.i(TAG, "Killing " + pr + ": remove task");
+ EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid,
+ pr.processName, pr.setAdj, "remove task");
+ Process.killProcessQuiet(pr.pid);
+ } else {
+ pr.waitingToKill = "remove task";
+ }
+ }
+ }
+
+ public boolean removeTask(int taskId, int flags) {
+ synchronized (this) {
+ enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+ "removeTask()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1);
+ if (r != null) {
+ mRecentTasks.remove(r.task);
+
+ if ((flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0) {
+ removeTaskProcessesLocked(r);
+ }
+
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ return false;
}
private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
@@ -5140,21 +5198,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final long origId = Binder.clearCallingIdentity();
try {
- int N = mRecentTasks.size();
- for (int i=0; i<N; i++) {
- TaskRecord tr = mRecentTasks.get(i);
- if (tr.taskId == task) {
- if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
- mMainStack.mUserLeaving = true;
- }
- if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
- // Caller wants the home activity moved with it. To accomplish this,
- // we'll just move the home task to the top first.
- mMainStack.moveHomeToFrontLocked();
- }
- mMainStack.moveTaskToFrontLocked(tr, null);
- return;
+ TaskRecord tr = taskForIdLocked(task);
+ if (tr != null) {
+ if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+ mMainStack.mUserLeaving = true;
+ }
+ if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+ // Caller wants the home activity moved with it. To accomplish this,
+ // we'll just move the home task to the top first.
+ mMainStack.moveHomeToFrontLocked();
}
+ mMainStack.moveTaskToFrontLocked(tr, null);
+ return;
}
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
@@ -5308,9 +5363,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
r = (ActivityRecord)mMainStack.mHistory.get(index);
}
- if (thumbnail == null) {
- thumbnail = r.thumbnail;
- description = r.description;
+ if (thumbnail == null && r.thumbHolder != null) {
+ thumbnail = r.thumbHolder.lastThumbnail;
+ description = r.thumbHolder.lastDescription;
}
if (thumbnail == null && !always) {
// If there is no thumbnail, and this entry is not actually
@@ -6678,11 +6733,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (app.pid > 0 && app.pid != MY_PID) {
handleAppCrashLocked(app);
- Slog.i(ActivityManagerService.TAG, "Killing "
- + app.processName + " (pid=" + app.pid + "): user's request");
+ Slog.i(ActivityManagerService.TAG, "Killing " + app + ": user's request");
EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
app.processName, app.setAdj, "user's request after error");
- Process.killProcess(app.pid);
+ Process.killProcessQuiet(app.pid);
}
}
}
@@ -7502,6 +7556,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
boolean dumpAll = false;
+ boolean dumpClient = false;
int opti = 0;
while (opti < args.length) {
@@ -7512,9 +7567,11 @@ public final class ActivityManagerService extends ActivityManagerNative
opti++;
if ("-a".equals(opt)) {
dumpAll = true;
+ } else if ("-c".equals(opt)) {
+ dumpClient = true;
} else if ("-h".equals(opt)) {
pw.println("Activity manager dump options:");
- pw.println(" [-a] [-h] [cmd] ...");
+ pw.println(" [-a] [-c] [-h] [cmd] ...");
pw.println(" cmd may be one of:");
pw.println(" a[ctivities]: activity stack state");
pw.println(" b[roadcasts]: broadcast state");
@@ -7523,10 +7580,14 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" o[om]: out of memory management");
pw.println(" prov[iders]: content provider state");
pw.println(" s[ervices]: service state");
- pw.println(" service [name]: service client-side state");
- pw.println(" cmd may also be a component name (com.foo/.myApp),");
- pw.println(" a partial substring in a component name, or an");
- pw.println(" ActivityRecord hex object identifier.");
+ pw.println(" service [COMP_SPEC]: service client-side state");
+ pw.println(" cmd may also be a COMP_SPEC to dump activities.");
+ pw.println(" COMP_SPEC may also be a component name (com.foo/.myApp),");
+ pw.println(" a partial substring in a component name, an");
+ pw.println(" ActivityRecord hex object identifier, or");
+ pw.println(" \"all\" for all objects");
+ pw.println(" -a: include all available server state.");
+ pw.println(" -c: include client state.");
return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
@@ -7539,7 +7600,7 @@ public final class ActivityManagerService extends ActivityManagerNative
opti++;
if ("activities".equals(cmd) || "a".equals(cmd)) {
synchronized (this) {
- dumpActivitiesLocked(fd, pw, args, opti, true, true);
+ dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient);
}
return;
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
@@ -7568,20 +7629,33 @@ public final class ActivityManagerService extends ActivityManagerNative
}
return;
} else if ("service".equals(cmd)) {
- dumpService(fd, pw, args, opti);
+ String[] newArgs;
+ String name;
+ if (opti >= args.length) {
+ name = null;
+ newArgs = EMPTY_STRING_ARRAY;
+ } else {
+ name = args[opti];
+ opti++;
+ newArgs = new String[args.length - opti];
+ if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+ }
+ if (!dumpService(fd, pw, name, newArgs, 0, dumpAll)) {
+ pw.println("No services match: " + name);
+ pw.println("Use -h for help.");
+ }
return;
} else if ("services".equals(cmd) || "s".equals(cmd)) {
synchronized (this) {
- dumpServicesLocked(fd, pw, args, opti, true);
+ dumpServicesLocked(fd, pw, args, opti, true, dumpClient);
}
return;
} else {
// Dumping a single activity?
- if (dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
- return;
+ if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
+ pw.println("Bad activity command, or no activities match: " + cmd);
+ pw.println("Use -h for help.");
}
- pw.println("Bad activity command, or no activities match: " + cmd);
- pw.println("Use -h for help.");
return;
}
}
@@ -7589,16 +7663,12 @@ public final class ActivityManagerService extends ActivityManagerNative
// No piece of data specified, dump everything.
synchronized (this) {
boolean needSep;
- if (dumpAll) {
- pw.println("Providers in Current Activity Manager State:");
- }
- needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll);
+ needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll);
if (needSep) {
pw.println(" ");
}
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
- pw.println("Broadcasts in Current Activity Manager State:");
}
needSep = dumpBroadcastsLocked(fd, pw, args, opti, dumpAll);
if (needSep) {
@@ -7606,88 +7676,95 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
- pw.println("Services in Current Activity Manager State:");
}
- needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll);
+ needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll);
if (needSep) {
pw.println(" ");
}
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
- pw.println("PendingIntents in Current Activity Manager State:");
}
- needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll);
+ needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient);
if (needSep) {
pw.println(" ");
}
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
- pw.println("Activities in Current Activity Manager State:");
}
- needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, !dumpAll);
+ needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient);
if (needSep) {
pw.println(" ");
}
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
- pw.println("Processes in Current Activity Manager State:");
}
dumpProcessesLocked(fd, pw, args, opti, dumpAll);
}
}
boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, boolean needHeader) {
- if (needHeader) {
- pw.println(" Activity stack:");
- }
- dumpHistoryList(pw, mMainStack.mHistory, " ", "Hist", true);
+ int opti, boolean dumpAll, boolean dumpClient) {
+ pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
+ pw.println(" Main stack:");
+ dumpHistoryList(fd, pw, mMainStack.mHistory, " ", "Hist", true, !dumpAll, dumpClient);
pw.println(" ");
pw.println(" Running activities (most recent first):");
- dumpHistoryList(pw, mMainStack.mLRUActivities, " ", "Run", false);
+ dumpHistoryList(fd, pw, mMainStack.mLRUActivities, " ", "Run", false, !dumpAll, false);
if (mMainStack.mWaitingVisibleActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting for another to become visible:");
- dumpHistoryList(pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false);
+ dumpHistoryList(fd, pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false,
+ !dumpAll, false);
}
if (mMainStack.mStoppingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to stop:");
- dumpHistoryList(pw, mMainStack.mStoppingActivities, " ", "Stop", false);
+ dumpHistoryList(fd, pw, mMainStack.mStoppingActivities, " ", "Stop", false,
+ !dumpAll, false);
}
if (mMainStack.mGoingToSleepActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to sleep:");
- dumpHistoryList(pw, mMainStack.mGoingToSleepActivities, " ", "Sleep", false);
+ dumpHistoryList(fd, pw, mMainStack.mGoingToSleepActivities, " ", "Sleep", false,
+ !dumpAll, false);
}
if (mMainStack.mFinishingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to finish:");
- dumpHistoryList(pw, mMainStack.mFinishingActivities, " ", "Fin", false);
+ dumpHistoryList(fd, pw, mMainStack.mFinishingActivities, " ", "Fin", false,
+ !dumpAll, false);
}
pw.println(" ");
- pw.println(" mPausingActivity: " + mMainStack.mPausingActivity);
+ if (mMainStack.mPausingActivity != null) {
+ pw.println(" mPausingActivity: " + mMainStack.mPausingActivity);
+ }
pw.println(" mResumedActivity: " + mMainStack.mResumedActivity);
pw.println(" mFocusedActivity: " + mFocusedActivity);
- pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity);
- pw.println(" mSleepTimeout: " + mMainStack.mSleepTimeout);
+ if (dumpAll) {
+ pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity);
+ pw.println(" mSleepTimeout: " + mMainStack.mSleepTimeout);
+ }
- if (dumpAll && mRecentTasks.size() > 0) {
- pw.println(" ");
- pw.println("Recent tasks in Current Activity Manager State:");
+ if (mRecentTasks.size() > 0) {
+ pw.println();
+ pw.println(" Recent tasks:");
final int N = mRecentTasks.size();
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
pw.print(" * Recent #"); pw.print(i); pw.print(": ");
pw.println(tr);
- mRecentTasks.get(i).dump(pw, " ");
+ if (dumpAll) {
+ mRecentTasks.get(i).dump(pw, " ");
+ }
}
}
- pw.println(" ");
- pw.println(" mCurTask: " + mCurTask);
+ if (dumpAll) {
+ pw.println(" ");
+ pw.println(" mCurTask: " + mCurTask);
+ }
return true;
}
@@ -7697,6 +7774,8 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean needSep = false;
int numPers = 0;
+ pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
+
if (dumpAll) {
for (SparseArray<ProcessRecord> procs : mProcessNames.getMap().values()) {
final int NA = procs.size();
@@ -7720,20 +7799,22 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mLruProcesses.size() > 0) {
if (needSep) pw.println(" ");
needSep = true;
- pw.println(" Running processes (most recent first):");
+ pw.println(" Process LRU list (most recent first):");
dumpProcessOomList(pw, this, mLruProcesses, " ",
"Proc", "PERS", false);
needSep = true;
}
- synchronized (mPidsSelfLocked) {
- if (mPidsSelfLocked.size() > 0) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" PID mappings:");
- for (int i=0; i<mPidsSelfLocked.size(); i++) {
- pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i));
- pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
+ if (dumpAll) {
+ synchronized (mPidsSelfLocked) {
+ if (mPidsSelfLocked.size() > 0) {
+ if (needSep) pw.println(" ");
+ needSep = true;
+ pw.println(" PID mappings:");
+ for (int i=0; i<mPidsSelfLocked.size(); i++) {
+ pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i));
+ pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
+ }
}
}
}
@@ -7818,16 +7899,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- pw.println(" ");
+ pw.println();
pw.println(" mHomeProcess: " + mHomeProcess);
if (mHeavyWeightProcess != null) {
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
}
pw.println(" mConfiguration: " + mConfiguration);
- pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange);
- if (mCompatModePackages.getPackages().size() > 0) {
- pw.print(" mScreenCompatPackages=");
- pw.println(mCompatModePackages.getPackages());
+ if (dumpAll) {
+ pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange);
+ if (mCompatModePackages.getPackages().size() > 0) {
+ pw.print(" mScreenCompatPackages=");
+ pw.println(mCompatModePackages.getPackages());
+ }
}
pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -7922,7 +8005,7 @@ public final class ActivityManagerService extends ActivityManagerNative
needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll);
- pw.println(" ");
+ pw.println();
pw.println(" mHomeProcess: " + mHomeProcess);
if (mHeavyWeightProcess != null) {
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
@@ -7939,61 +8022,96 @@ public final class ActivityManagerService extends ActivityManagerNative
* - the first arg isn't the flattened component name of an existing service:
* dump all services whose component contains the first arg as a substring
*/
- protected void dumpService(FileDescriptor fd, PrintWriter pw, String[] args, int opti) {
- String[] newArgs;
- String componentNameString;
- ServiceRecord r;
- if (opti >= args.length) {
- componentNameString = null;
- newArgs = EMPTY_STRING_ARRAY;
- r = null;
- } else {
- componentNameString = args[opti];
- opti++;
- ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
+ protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+ int opti, boolean dumpAll) {
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+
+ if ("all".equals(name)) {
synchronized (this) {
- r = componentName != null ? mServices.get(componentName) : null;
+ for (ServiceRecord r1 : mServices.values()) {
+ services.add(r1);
+ }
}
- 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);
} else {
- ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ ComponentName componentName = name != null
+ ? ComponentName.unflattenFromString(name) : null;
+ int objectId = 0;
+ if (componentName == null) {
+ // Not a '/' separated full component name; maybe an object ID?
+ try {
+ objectId = Integer.parseInt(name, 16);
+ name = null;
+ componentName = null;
+ } catch (RuntimeException e) {
+ }
+ }
+
synchronized (this) {
for (ServiceRecord r1 : mServices.values()) {
- if (componentNameString == null
- || r1.name.flattenToString().contains(componentNameString)) {
+ if (componentName != null) {
+ if (r1.name.equals(componentName)) {
+ services.add(r1);
+ }
+ } else if (name != null) {
+ if (r1.name.flattenToString().contains(name)) {
+ services.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
services.add(r1);
}
}
}
- for (int i=0; i<services.size(); i++) {
- dumpService(fd, pw, services.get(i), newArgs);
+ }
+
+ if (services.size() <= 0) {
+ return false;
+ }
+
+ boolean needSep = false;
+ for (int i=0; i<services.size(); i++) {
+ if (needSep) {
+ pw.println();
}
+ needSep = true;
+ dumpService("", fd, pw, services.get(i), args, dumpAll);
}
+ return true;
}
/**
* 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) {
- pw.println("------------------------------------------------------------"
- + "-------------------");
- pw.println("APP SERVICE: " + r.name.flattenToString());
+ private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw,
+ final ServiceRecord r, String[] args, boolean dumpAll) {
+ String innerPrefix = prefix + " ";
+ synchronized (this) {
+ pw.print(prefix); pw.print("SERVICE ");
+ pw.print(r.shortName); pw.print(" ");
+ pw.print(Integer.toHexString(System.identityHashCode(r)));
+ pw.print(" pid=");
+ if (r.app != null) pw.println(r.app.pid);
+ else pw.println("(not running)");
+ if (dumpAll) {
+ r.dump(pw, innerPrefix);
+ }
+ }
if (r.app != null && r.app.thread != null) {
+ pw.print(prefix); pw.println(" Client:");
+ pw.flush();
try {
- // flush anything that is already in the PrintWriter since the thread is going
- // to write to the file descriptor directly
- pw.flush();
- r.app.thread.dumpService(fd, r, args);
- pw.print("\n");
- pw.flush();
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args);
+ tp.setBufferPrefix(prefix + " ");
+ tp.go(fd);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(prefix + " Failure while dumping the service: " + e);
} catch (RemoteException e) {
- pw.println("got a RemoteException while dumping the service");
+ pw.println(prefix + " Got a RemoteException while dumping the service");
}
}
}
@@ -8007,34 +8125,40 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
int opti, boolean dumpAll) {
- String[] newArgs;
- ComponentName componentName = ComponentName.unflattenFromString(name);
- int objectId = 0;
- if (componentName == null) {
- // Not a '/' separated full component name; maybe an object ID?
- try {
- objectId = Integer.parseInt(name, 16);
- name = null;
- componentName = null;
- } catch (RuntimeException e) {
+ ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+
+ if ("all".equals(name)) {
+ synchronized (this) {
+ for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
+ activities.add(r1);
+ }
+ }
+ } else {
+ ComponentName componentName = ComponentName.unflattenFromString(name);
+ int objectId = 0;
+ if (componentName == null) {
+ // Not a '/' separated full component name; maybe an object ID?
+ try {
+ objectId = Integer.parseInt(name, 16);
+ name = null;
+ componentName = null;
+ } catch (RuntimeException e) {
+ }
}
- }
- newArgs = new String[args.length - opti];
- if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
- ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
- synchronized (this) {
- for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
- if (componentName != null) {
- if (r1.intent.getComponent().equals(componentName)) {
- activities.add(r1);
- }
- } else if (name != null) {
- if (r1.intent.getComponent().flattenToString().contains(name)) {
+ synchronized (this) {
+ for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
+ if (componentName != null) {
+ if (r1.intent.getComponent().equals(componentName)) {
+ activities.add(r1);
+ }
+ } else if (name != null) {
+ if (r1.intent.getComponent().flattenToString().contains(name)) {
+ activities.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
activities.add(r1);
}
- } else if (System.identityHashCode(r1) == objectId) {
- activities.add(r1);
}
}
}
@@ -8043,15 +8167,25 @@ public final class ActivityManagerService extends ActivityManagerNative
return false;
}
+ String[] newArgs = new String[args.length - opti];
+ if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+
TaskRecord lastTask = null;
+ boolean needSep = false;
for (int i=activities.size()-1; i>=0; i--) {
ActivityRecord r = (ActivityRecord)activities.get(i);
- if (lastTask != r.task) {
- lastTask = r.task;
- pw.print("* Task "); pw.print(lastTask.affinity);
- pw.print(" id="); pw.println(lastTask.taskId);
- if (dumpAll) {
- lastTask.dump(pw, " ");
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ synchronized (this) {
+ if (lastTask != r.task) {
+ lastTask = r.task;
+ pw.print("TASK "); pw.print(lastTask.affinity);
+ pw.print(" id="); pw.println(lastTask.taskId);
+ if (dumpAll) {
+ lastTask.dump(pw, " ");
+ }
}
}
dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll);
@@ -8064,26 +8198,35 @@ public final class ActivityManagerService extends ActivityManagerNative
* there is a thread associated with the activity.
*/
private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
- ActivityRecord r, String[] args, boolean dumpAll) {
+ final ActivityRecord r, String[] args, boolean dumpAll) {
+ String innerPrefix = prefix + " ";
synchronized (this) {
- pw.print(prefix); pw.print("* Activity ");
- pw.print(Integer.toHexString(System.identityHashCode(r)));
- pw.print(" "); pw.print(r.shortComponentName); pw.print(" pid=");
+ pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
+ pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
+ pw.print(" pid=");
if (r.app != null) pw.println(r.app.pid);
else pw.println("(not running)");
if (dumpAll) {
- r.dump(pw, prefix + " ");
+ r.dump(pw, innerPrefix);
}
}
if (r.app != null && r.app.thread != null) {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
try {
- // flush anything that is already in the PrintWriter since the thread is going
- // to write to the file descriptor directly
- pw.flush();
- r.app.thread.dumpActivity(fd, r, prefix + " ", args);
- pw.flush();
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r,
+ innerPrefix, args);
+ tp.go(fd);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(innerPrefix + "Failure while dumping the activity: " + e);
} catch (RemoteException e) {
- pw.println("got a RemoteException while dumping the activity");
+ pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
}
}
}
@@ -8092,9 +8235,9 @@ public final class ActivityManagerService extends ActivityManagerNative
int opti, boolean dumpAll) {
boolean needSep = false;
+ pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)");
if (dumpAll) {
if (mRegisteredReceivers.size() > 0) {
- pw.println(" ");
pw.println(" Registered Receivers:");
Iterator it = mRegisteredReceivers.values().iterator();
while (it.hasNext()) {
@@ -8104,16 +8247,16 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- pw.println(" ");
- pw.println("Receiver Resolver Table:");
- mReceiverResolver.dump(pw, null, " ", null, false);
+ pw.println();
+ pw.println(" Receiver Resolver Table:");
+ mReceiverResolver.dump(pw, null, " ", null, false);
needSep = true;
}
if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
|| mPendingBroadcast != null) {
if (mParallelBroadcasts.size() > 0) {
- pw.println(" ");
+ pw.println();
pw.println(" Active broadcasts:");
}
for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
@@ -8121,14 +8264,14 @@ public final class ActivityManagerService extends ActivityManagerNative
mParallelBroadcasts.get(i).dump(pw, " ");
}
if (mOrderedBroadcasts.size() > 0) {
- pw.println(" ");
+ pw.println();
pw.println(" Active ordered broadcasts:");
}
for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) {
pw.println(" Serialized Broadcast #" + i + ":");
mOrderedBroadcasts.get(i).dump(pw, " ");
}
- pw.println(" ");
+ pw.println();
pw.println(" Pending broadcast:");
if (mPendingBroadcast != null) {
mPendingBroadcast.dump(pw, " ");
@@ -8138,47 +8281,59 @@ public final class ActivityManagerService extends ActivityManagerNative
needSep = true;
}
- if (dumpAll) {
- pw.println(" ");
- pw.println(" Historical broadcasts:");
- for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
- BroadcastRecord r = mBroadcastHistory[i];
- if (r == null) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" Historical broadcasts:");
+ for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
+ BroadcastRecord r = mBroadcastHistory[i];
+ if (r == null) {
+ break;
+ }
+ if (dumpAll) {
+ pw.print(" Historical Broadcast #"); pw.print(i); pw.println(":");
+ r.dump(pw, " ");
+ } else {
+ if (i >= 50) {
+ pw.println(" ...");
break;
}
- pw.println(" Historical Broadcast #" + i + ":");
- r.dump(pw, " ");
+ pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
}
- needSep = true;
}
+ needSep = true;
if (mStickyBroadcasts != null) {
- pw.println(" ");
+ pw.println();
pw.println(" Sticky broadcasts:");
StringBuilder sb = new StringBuilder(128);
for (Map.Entry<String, ArrayList<Intent>> ent
: mStickyBroadcasts.entrySet()) {
pw.print(" * Sticky action "); pw.print(ent.getKey());
- pw.println(":");
- ArrayList<Intent> intents = ent.getValue();
- final int N = intents.size();
- for (int i=0; i<N; i++) {
- sb.setLength(0);
- sb.append(" Intent: ");
- intents.get(i).toShortString(sb, true, false);
- pw.println(sb.toString());
- Bundle bundle = intents.get(i).getExtras();
- if (bundle != null) {
- pw.print(" ");
- pw.println(bundle.toString());
+ if (dumpAll) {
+ pw.println(":");
+ ArrayList<Intent> intents = ent.getValue();
+ final int N = intents.size();
+ for (int i=0; i<N; i++) {
+ sb.setLength(0);
+ sb.append(" Intent: ");
+ intents.get(i).toShortString(sb, true, false);
+ pw.println(sb.toString());
+ Bundle bundle = intents.get(i).getExtras();
+ if (bundle != null) {
+ pw.print(" ");
+ pw.println(bundle.toString());
+ }
}
+ } else {
+ pw.println("");
}
}
needSep = true;
}
if (dumpAll) {
- pw.println(" ");
+ pw.println();
pw.println(" mBroadcastsScheduled=" + mBroadcastsScheduled);
pw.println(" mHandler:");
mHandler.dump(new PrintWriterPrinter(pw), " ");
@@ -8189,20 +8344,55 @@ public final class ActivityManagerService extends ActivityManagerNative
}
boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll) {
+ int opti, boolean dumpAll, boolean dumpClient) {
boolean needSep = false;
- if (dumpAll) {
- if (mServices.size() > 0) {
- pw.println(" Active services:");
- Iterator<ServiceRecord> it = mServices.values().iterator();
- while (it.hasNext()) {
- ServiceRecord r = it.next();
- pw.print(" * "); pw.println(r);
+ pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
+ if (mServices.size() > 0) {
+ pw.println(" Active services:");
+ long nowReal = SystemClock.elapsedRealtime();
+ Iterator<ServiceRecord> it = mServices.values().iterator();
+ needSep = false;
+ while (it.hasNext()) {
+ ServiceRecord r = it.next();
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(" * "); pw.println(r);
+ if (dumpAll) {
r.dump(pw, " ");
+ needSep = true;
+ } else {
+ pw.print(" app="); pw.println(r.app);
+ pw.print(" created=");
+ TimeUtils.formatDuration(r.createTime, nowReal, pw);
+ pw.print(" started="); pw.print(r.startRequested);
+ pw.print(" connections="); pw.println(r.connections.size());
+ }
+ if (dumpClient && r.app != null && r.app.thread != null) {
+ pw.println(" Client:");
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpService(
+ tp.getWriteFd().getFileDescriptor(), r, args);
+ tp.setBufferPrefix(" ");
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(" Failure while dumping the service: " + e);
+ } catch (RemoteException e) {
+ pw.println(" Got a RemoteException while dumping the service");
+ }
+ needSep = true;
}
- needSep = true;
}
+ needSep = true;
}
if (mPendingServices.size() > 0) {
@@ -8262,21 +8452,32 @@ public final class ActivityManagerService extends ActivityManagerNative
int opti, boolean dumpAll) {
boolean needSep = false;
- if (dumpAll) {
- if (mProvidersByClass.size() > 0) {
- if (needSep) pw.println(" ");
- pw.println(" Published content providers (by class):");
- Iterator<Map.Entry<String, ContentProviderRecord>> it
- = mProvidersByClass.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, ContentProviderRecord> e = it.next();
- ContentProviderRecord r = e.getValue();
+ pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
+ if (mProvidersByClass.size() > 0) {
+ if (needSep) pw.println(" ");
+ pw.println(" Published content providers (by class):");
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByClass.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
+ if (dumpAll) {
pw.print(" * "); pw.println(r);
r.dump(pw, " ");
+ } else {
+ pw.print(" * "); pw.print(r.name.toShortString());
+ if (r.app != null) {
+ pw.println(":");
+ pw.print(" "); pw.println(r.app);
+ } else {
+ pw.println();
+ }
}
- needSep = true;
}
+ needSep = true;
+ }
+ if (dumpAll) {
if (mProvidersByName.size() > 0) {
pw.println(" ");
pw.println(" Authority to provider mappings:");
@@ -8313,7 +8514,9 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" holds:");
for (UriPermission perm : perms.values()) {
pw.print(" "); pw.println(perm);
- perm.dump(pw, " ");
+ if (dumpAll) {
+ perm.dump(pw, " ");
+ }
}
}
needSep = true;
@@ -8326,20 +8529,21 @@ public final class ActivityManagerService extends ActivityManagerNative
int opti, boolean dumpAll) {
boolean needSep = false;
- if (dumpAll) {
- if (this.mIntentSenderRecords.size() > 0) {
- Iterator<WeakReference<PendingIntentRecord>> it
- = mIntentSenderRecords.values().iterator();
- while (it.hasNext()) {
- WeakReference<PendingIntentRecord> ref = it.next();
- PendingIntentRecord rec = ref != null ? ref.get(): null;
- needSep = true;
- if (rec != null) {
- pw.print(" * "); pw.println(rec);
+ if (this.mIntentSenderRecords.size() > 0) {
+ pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
+ Iterator<WeakReference<PendingIntentRecord>> it
+ = mIntentSenderRecords.values().iterator();
+ while (it.hasNext()) {
+ WeakReference<PendingIntentRecord> ref = it.next();
+ PendingIntentRecord rec = ref != null ? ref.get(): null;
+ needSep = true;
+ if (rec != null) {
+ pw.print(" * "); pw.println(rec);
+ if (dumpAll) {
rec.dump(pw, " ");
- } else {
- pw.print(" * "); pw.print(ref);
}
+ } else {
+ pw.print(" * "); pw.println(ref);
}
}
}
@@ -8347,12 +8551,19 @@ public final class ActivityManagerService extends ActivityManagerNative
return needSep;
}
- private static final void dumpHistoryList(PrintWriter pw, List list,
- String prefix, String label, boolean complete) {
+ private static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List list,
+ String prefix, String label, boolean complete, boolean brief, boolean client) {
TaskRecord lastTask = null;
+ boolean needNL = false;
+ final String innerPrefix = prefix + " ";
+ final String[] args = new String[0];
for (int i=list.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)list.get(i);
- final boolean full = complete || !r.inHistory;
+ final ActivityRecord r = (ActivityRecord)list.get(i);
+ final boolean full = !brief && (complete || !r.isInHistory());
+ if (needNL) {
+ pw.println(" ");
+ needNL = false;
+ }
if (lastTask != r.task) {
lastTask = r.task;
pw.print(prefix);
@@ -8360,13 +8571,46 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(lastTask);
if (full) {
lastTask.dump(pw, prefix + " ");
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ if (lastTask.intent != null) {
+ pw.print(prefix); pw.print(" "); pw.println(lastTask.intent);
+ }
}
}
pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
pw.print(" #"); pw.print(i); pw.print(": ");
pw.println(r);
if (full) {
- r.dump(pw, prefix + " ");
+ r.dump(pw, innerPrefix);
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ pw.print(innerPrefix); pw.println(r.intent);
+ if (r.app != null) {
+ pw.print(innerPrefix); pw.println(r.app);
+ }
+ }
+ if (client && r.app != null && r.app.thread != null) {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r,
+ innerPrefix, args);
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+ } catch (RemoteException e) {
+ pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+ }
+ needNL = true;
}
}
}
@@ -8452,7 +8696,7 @@ public final class ActivityManagerService extends ActivityManagerNative
N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
if (r.adjSource != null || r.adjTarget != null) {
pw.print(prefix);
- pw.print(" ");
+ pw.print(" ");
if (r.adjTarget instanceof ComponentName) {
pw.print(((ComponentName)r.adjTarget).flattenToShortString());
} else if (r.adjTarget != null) {
@@ -8522,8 +8766,75 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- static final void dumpApplicationMemoryUsage(FileDescriptor fd,
- PrintWriter pw, List list, String prefix, String[] args) {
+ ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, String[] args) {
+ ArrayList<ProcessRecord> procs;
+ synchronized (this) {
+ if (args != null && args.length > 0
+ && args[0].charAt(0) != '-') {
+ procs = new ArrayList<ProcessRecord>();
+ int pid = -1;
+ try {
+ pid = Integer.parseInt(args[0]);
+ } catch (NumberFormatException e) {
+
+ }
+ for (int i=mLruProcesses.size()-1; i>=0; i--) {
+ ProcessRecord proc = mLruProcesses.get(i);
+ if (proc.pid == pid) {
+ procs.add(proc);
+ } else if (proc.processName.equals(args[0])) {
+ procs.add(proc);
+ }
+ }
+ if (procs.size() <= 0) {
+ pw.println("No process found for: " + args[0]);
+ return null;
+ }
+ } else {
+ procs = new ArrayList<ProcessRecord>(mLruProcesses);
+ }
+ }
+ return procs;
+ }
+
+ final void dumpGraphicsHardwareUsage(FileDescriptor fd,
+ PrintWriter pw, String[] args) {
+ ArrayList<ProcessRecord> procs = collectProcesses(pw, args);
+ if (procs == null) {
+ return;
+ }
+
+ long uptime = SystemClock.uptimeMillis();
+ long realtime = SystemClock.elapsedRealtime();
+ pw.println("Applications Graphics Acceleration Info:");
+ pw.println("Uptime: " + uptime + " Realtime: " + realtime);
+
+ String callArgs[] = {"graphics"};
+ for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = procs.get(i);
+ if (r.thread != null) {
+ pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
+ pw.flush();
+ try {
+ TransferPipe.goDump(r.thread.asBinder(), fd, callArgs);
+ } catch (IOException e) {
+ pw.println("Failure: " + e);
+ pw.flush();
+ } catch (RemoteException e) {
+ pw.println("Got RemoteException!");
+ pw.flush();
+ }
+ }
+ }
+ }
+
+ final void dumpApplicationMemoryUsage(FileDescriptor fd,
+ PrintWriter pw, String prefix, String[] args) {
+ ArrayList<ProcessRecord> procs = collectProcesses(pw, args);
+ if (procs == null) {
+ return;
+ }
+
final boolean isCheckinRequest = scanArgs(args, "--checkin");
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
@@ -8536,15 +8847,18 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println("Applications Memory Usage (kB):");
pw.println("Uptime: " + uptime + " Realtime: " + realtime);
}
- for (int i = list.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = (ProcessRecord)list.get(i);
+ for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = procs.get(i);
if (r.thread != null) {
if (!isCheckinRequest) {
pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
pw.flush();
}
try {
- r.thread.asBinder().dump(fd, args);
+ TransferPipe.goDump(r.thread.asBinder(), fd, args);
+ } catch (IOException e) {
+ pw.println("Failure: " + e);
+ pw.flush();
} catch (RemoteException e) {
if (!isCheckinRequest) {
pw.println("Got RemoteException!");
@@ -8714,7 +9028,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " in dying process " + proc.processName);
EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
capp.processName, capp.setAdj, "dying provider " + proc.processName);
- Process.killProcess(capp.pid);
+ Process.killProcessQuiet(capp.pid);
}
}
@@ -9221,7 +9535,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (si.doneExecutingCount > 0) {
flags |= Service.START_FLAG_REDELIVERY;
}
- r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
+ r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
} catch (RemoteException e) {
// Remote process gone... we'll let the normal cleanup take
// care of this.
@@ -9308,11 +9622,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// pending arguments, then fake up one so its onStartCommand() will
// be called.
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
- r.lastStartId++;
- if (r.lastStartId < 1) {
- r.lastStartId = 1;
- }
- r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
+ null, -1));
}
sendServiceArgsLocked(r, true);
@@ -9666,11 +9977,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
r.startRequested = true;
r.callStart = false;
- r.lastStartId++;
- if (r.lastStartId < 1) {
- r.lastStartId = 1;
- }
- r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, targetPermissionUid));
r.lastActivity = SystemClock.uptimeMillis();
synchronized (r.stats.getBatteryStats()) {
@@ -9712,6 +10019,15 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ private void stopServiceLocked(ServiceRecord service) {
+ synchronized (service.stats.getBatteryStats()) {
+ service.stats.stopRunningLocked();
+ }
+ service.startRequested = false;
+ service.callStart = false;
+ bringDownServiceLocked(service, false);
+ }
+
public int stopService(IApplicationThread caller, Intent service,
String resolvedType) {
// Refuse possible leaked file descriptors
@@ -9735,14 +10051,12 @@ public final class ActivityManagerService extends ActivityManagerNative
ServiceLookupResult r = findServiceLocked(service, resolvedType);
if (r != null) {
if (r.record != null) {
- synchronized (r.record.stats.getBatteryStats()) {
- r.record.stats.stopRunningLocked();
- }
- r.record.startRequested = false;
- r.record.callStart = false;
final long origId = Binder.clearCallingIdentity();
- bringDownServiceLocked(r.record, false);
- Binder.restoreCallingIdentity(origId);
+ try {
+ stopServiceLocked(r.record);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
return 1;
}
return -1;
@@ -9804,7 +10118,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- if (r.lastStartId != startId) {
+ if (r.getLastStartId() != startId) {
return false;
}
@@ -10245,7 +10559,7 @@ public final class ActivityManagerService extends ActivityManagerNative
case Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
- if (r.lastStartId == startId) {
+ if (r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
r.stopIfKilled = true;
@@ -10265,6 +10579,12 @@ public final class ActivityManagerService extends ActivityManagerNative
}
break;
}
+ case Service.START_TASK_REMOVED_COMPLETE: {
+ // Special processing for onTaskRemoved(). Don't
+ // impact normal onStartCommand() processing.
+ r.findDeliveredStart(startId, true);
+ break;
+ }
default:
throw new IllegalArgumentException(
"Unknown service start result: " + res);
@@ -10375,7 +10695,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
BackupRecord r = new BackupRecord(ss, app, backupMode);
- ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName);
+ ComponentName hostingName = (backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL)
+ ? new ComponentName(app.packageName, app.backupAgentName)
+ : new ComponentName("android", "FullBackupAgent");
// startProcessLocked() returns existing proc's record if it's already running
ProcessRecord proc = startProcessLocked(app.processName, app,
false, 0, "backup", hostingName, false);
@@ -12669,23 +12991,31 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
"Setting process group of " + app.processName
+ " to " + app.curSchedGroup);
- if (true) {
- long oldId = Binder.clearCallingIdentity();
- try {
- Process.setProcessGroup(app.pid, app.curSchedGroup);
- } catch (Exception e) {
- Slog.w(TAG, "Failed setting process group of " + app.pid
- + " to " + app.curSchedGroup);
- e.printStackTrace();
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
- if (false) {
- if (app.thread != null) {
+ if (app.waitingToKill != null &&
+ app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ Slog.i(TAG, "Killing " + app + ": " + app.waitingToKill);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, app.waitingToKill);
+ Process.killProcessQuiet(app.pid);
+ } else {
+ if (true) {
+ long oldId = Binder.clearCallingIdentity();
try {
- app.thread.setSchedulingGroup(app.curSchedGroup);
- } catch (RemoteException e) {
+ Process.setProcessGroup(app.pid, app.curSchedGroup);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed setting process group of " + app.pid
+ + " to " + app.curSchedGroup);
+ e.printStackTrace();
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+ if (false) {
+ if (app.thread != null) {
+ try {
+ app.thread.setSchedulingGroup(app.curSchedGroup);
+ } catch (RemoteException e) {
+ }
}
}
}
@@ -12808,7 +13138,9 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (app.thread != null ? app.thread.asBinder() : null)
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
- Process.killProcess(app.pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "empty");
+ Process.killProcessQuiet(app.pid);
} else {
try {
app.thread.scheduleExit();
@@ -12874,7 +13206,9 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (app.thread != null ? app.thread.asBinder() : null)
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
- Process.killProcess(app.pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "empty");
+ Process.killProcessQuiet(app.pid);
} else {
try {
app.thread.scheduleExit();
@@ -12893,7 +13227,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// If we still have too many processes, now from the least
// recently used process we start finishing activities.
- if (Config.LOGV) Slog.v(
+ if (false) Slog.v(
TAG, "*** NOW HAVE " + mLruProcesses.size() +
" of " + curMaxProcs + " processes");
for ( i=0;
@@ -12907,11 +13241,11 @@ public final class ActivityManagerService extends ActivityManagerNative
&& app.services.size() == 0;
int NUMA = app.activities.size();
int j;
- if (Config.LOGV) Slog.v(
+ if (false) Slog.v(
TAG, "Looking to quit " + app.processName);
for (j=0; j<NUMA && canQuit; j++) {
ActivityRecord r = app.activities.get(j);
- if (Config.LOGV) Slog.v(
+ if (false) Slog.v(
TAG, " " + r.intent.getComponent().flattenToShortString()
+ ": frozen=" + r.haveState + ", visible=" + r.visible);
canQuit = (r.haveState || !r.stateNotNeeded)
@@ -12931,7 +13265,9 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (app.thread != null ? app.thread.asBinder() : null)
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
- Process.killProcess(app.pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "old background");
+ Process.killProcessQuiet(app.pid);
} else {
try {
app.thread.scheduleExit();
@@ -13148,4 +13484,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
+
+ // Multi-user methods
+
+ public boolean switchUser(int userid) {
+ // TODO
+ return true;
+ }
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index cb0a0f0..cc9e78e 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -47,7 +47,7 @@ import java.util.HashSet;
/**
* An entry in the history stack, representing an activity.
*/
-class ActivityRecord extends IApplicationToken.Stub {
+final class ActivityRecord extends IApplicationToken.Stub {
final ActivityManagerService service; // owner
final ActivityStack stack; // owner
final ActivityInfo info; // all about me
@@ -74,6 +74,7 @@ class ActivityRecord extends IApplicationToken.Stub {
int realTheme; // actual theme resource we will use, never 0.
int windowFlags; // custom window flags for preview window.
TaskRecord task; // the task this is in.
+ ThumbnailHolder thumbHolder; // where our thumbnails should go.
long launchTime; // when we starting launching this activity
long startTime; // last time this activity was started
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
@@ -86,9 +87,7 @@ class ActivityRecord extends IApplicationToken.Stub {
ArrayList newIntents; // any pending new intents for single-top mode
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
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
+ ProcessRecord app; // if non-null, hosting application
ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
boolean frontOfTask; // is this the root activity of its task?
@@ -100,7 +99,6 @@ class ActivityRecord extends IApplicationToken.Stub {
boolean configDestroy; // need to destroy due to config change?
int configChangeFlags; // which config values have changed
boolean keysPaused; // has key dispatching been paused for it?
- boolean inHistory; // are we in the history stack?
int launchMode; // the launch mode activity attribute.
boolean visible; // does this activity's window need to be shown?
boolean sleeping; // have we told the activity to sleep?
@@ -115,6 +113,8 @@ class ActivityRecord extends IApplicationToken.Stub {
String stringName; // for caching of toString().
+ private boolean inHistory; // are we in the history stack?
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
@@ -136,7 +136,7 @@ class ActivityRecord extends IApplicationToken.Stub {
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
pw.print(" isHomeActivity="); pw.println(isHomeActivity);
- pw.print(prefix); pw.print("configuration="); pw.println(configuration);
+ pw.print(prefix); pw.print("config="); pw.println(configuration);
if (resultTo != null || resultWho != null) {
pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
pw.print(" resultWho="); pw.print(resultWho);
@@ -177,6 +177,7 @@ class ActivityRecord extends IApplicationToken.Stub {
pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
pw.print(" forceNewConfig="); pw.println(forceNewConfig);
+ pw.print(prefix); pw.print("thumbHolder="); pw.println(thumbHolder);
if (launchTime != 0 || startTime != 0) {
pw.print(prefix); pw.print("launchTime=");
TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime=");
@@ -330,10 +331,55 @@ class ActivityRecord extends IApplicationToken.Stub {
}
}
+ void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
+ if (inHistory && !finishing) {
+ if (task != null) {
+ task.numActivities--;
+ }
+ if (newTask != null) {
+ newTask.numActivities++;
+ }
+ }
+ if (newThumbHolder == null) {
+ newThumbHolder = newTask;
+ }
+ task = newTask;
+ if (!isRoot && (intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // This is the start of a new sub-task.
+ if (thumbHolder == null) {
+ thumbHolder = new ThumbnailHolder();
+ }
+ } else {
+ thumbHolder = newThumbHolder;
+ }
+ }
+
+ void putInHistory() {
+ if (!inHistory) {
+ inHistory = true;
+ if (task != null && !finishing) {
+ task.numActivities++;
+ }
+ }
+ }
+
+ void takeFromHistory() {
+ if (inHistory) {
+ inHistory = false;
+ if (task != null && !finishing) {
+ task.numActivities--;
+ }
+ }
+ }
+
+ boolean isInHistory() {
+ return inHistory;
+ }
+
void makeFinishing() {
if (!finishing) {
finishing = true;
- if (task != null) {
+ if (task != null && inHistory) {
task.numActivities--;
}
}
@@ -432,6 +478,25 @@ class ActivityRecord extends IApplicationToken.Stub {
}
}
+ void updateThumbnail(Bitmap newThumbnail, CharSequence description) {
+ if ((intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // This is a logical break in the task; it repre
+ }
+ if (thumbHolder != null) {
+ if (newThumbnail != null) {
+ thumbHolder.lastThumbnail = newThumbnail;
+ }
+ thumbHolder.lastDescription = description;
+ }
+ }
+
+ void clearThumbnail() {
+ if (thumbHolder != null) {
+ thumbHolder.lastThumbnail = null;
+ thumbHolder.lastDescription = null;
+ }
+ }
+
// IApplicationToken
public boolean mayFreezeScreenLocked(ProcessRecord app) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index f385042..d5ac19e 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -21,8 +21,10 @@ import com.android.internal.os.BatteryStatsImpl;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.IActivityManager;
+import android.app.IThumbnailRetriever;
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;
@@ -69,9 +71,9 @@ import java.util.List;
/**
* State and management of a single stack of activities.
*/
-public class ActivityStack {
+final class ActivityStack {
static final String TAG = ActivityManagerService.TAG;
- static final boolean localLOGV = ActivityManagerService.localLOGV;
+ static final boolean localLOGV = ActivityManagerService.localLOGV || true;
static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE;
static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY;
@@ -135,14 +137,14 @@ public class ActivityStack {
* The back history of all previous (and possibly still
* running) activities. It contains HistoryRecord objects.
*/
- final ArrayList mHistory = new ArrayList();
+ final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();
/**
* 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();
+ final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<ActivityRecord>();
/**
* List of activities that are waiting for a new activity
@@ -346,7 +348,7 @@ public class ActivityStack {
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
int i = mHistory.size()-1;
while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (!r.finishing && r != notTop) {
return r;
}
@@ -358,7 +360,7 @@ public class ActivityStack {
final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
int i = mHistory.size()-1;
while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (!r.finishing && !r.delayedResume && r != notTop) {
return r;
}
@@ -379,7 +381,7 @@ public class ActivityStack {
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
int i = mHistory.size()-1;
while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
// Note: the taskId check depends on real taskId fields being non-zero
if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
return r;
@@ -425,7 +427,7 @@ public class ActivityStack {
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (!r.finishing && r.task != cp
&& r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
cp = r.task;
@@ -469,7 +471,7 @@ public class ActivityStack {
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (!r.finishing) {
if (r.intent.getComponent().equals(cls)) {
//Slog.i(TAG, "Found matching class!");
@@ -504,6 +506,7 @@ public class ActivityStack {
}
r.app = app;
+ app.waitingToKill = null;
if (localLOGV) Slog.v(TAG, "Launching: " + r);
@@ -680,7 +683,7 @@ public class ActivityStack {
}
// Ensure activities are no longer sleeping.
for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
r.setSleeping(false);
}
mGoingToSleepActivities.clear();
@@ -726,7 +729,7 @@ public class ActivityStack {
// Make sure any stopped but visible activities are now sleeping.
// This ensures that the activity's onStop() is called.
for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
r.setSleeping(true);
}
@@ -791,10 +794,7 @@ public class ActivityStack {
mLastPausedActivity = prev;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
- prev.thumbnail = screenshotActivities(prev);
- if (prev.task != null) {
- prev.task.lastThumbnail = prev.thumbnail;
- }
+ prev.updateThumbnail(screenshotActivities(prev), null);
mService.updateCpuStats();
@@ -867,7 +867,7 @@ public class ActivityStack {
synchronized (mService) {
int index = indexOfTokenLocked(token);
if (index >= 0) {
- r = (ActivityRecord)mHistory.get(index);
+ r = mHistory.get(index);
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
r.state = ActivityState.PAUSED;
@@ -988,7 +988,7 @@ public class ActivityStack {
mService.reportResumedActivityLocked(next);
}
- next.thumbnail = null;
+ next.clearThumbnail();
if (mMainStack) {
mService.setFocusedActivityLocked(next);
}
@@ -1029,7 +1029,7 @@ public class ActivityStack {
ActivityRecord r;
boolean behindFullscreen = false;
for (; i>=0; i--) {
- r = (ActivityRecord)mHistory.get(i);
+ r = mHistory.get(i);
if (DEBUG_VISBILITY) Slog.v(
TAG, "Make visible? " + r + " finishing=" + r.finishing
+ " state=" + r.state);
@@ -1113,7 +1113,7 @@ public class ActivityStack {
// 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);
+ r = mHistory.get(i);
if (DEBUG_VISBILITY) Slog.v(
TAG, "Make invisible? " + r + " finishing=" + r.finishing
+ " state=" + r.state
@@ -1504,7 +1504,7 @@ public class ActivityStack {
// If starting in an existing task, find where that is...
boolean startIt = true;
for (int i = NH-1; i >= 0; i--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ ActivityRecord p = mHistory.get(i);
if (p.finishing) {
continue;
}
@@ -1515,8 +1515,7 @@ public class ActivityStack {
addPos = i+1;
if (!startIt) {
mHistory.add(addPos, r);
- r.inHistory = true;
- r.task.numActivities++;
+ r.putInHistory();
mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
r.info.screenOrientation, r.fullscreen);
if (VALIDATE_TOKENS) {
@@ -1548,9 +1547,8 @@ public class ActivityStack {
// Slot the activity into the history stack and proceed
mHistory.add(addPos, r);
- r.inHistory = true;
+ r.putInHistory();
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
@@ -1653,7 +1651,7 @@ public class ActivityStack {
int replyChainEnd = -1;
int lastReparentPos = -1;
for (int i=mHistory.size()-1; i>=-1; i--) {
- ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
+ ActivityRecord below = i >= 0 ? mHistory.get(i) : null;
if (below != null && below.finishing) {
continue;
@@ -1707,13 +1705,13 @@ public class ActivityStack {
// bottom of the activity stack. This also keeps it
// correctly ordered with any activities we previously
// moved.
- ActivityRecord p = (ActivityRecord)mHistory.get(0);
+ ActivityRecord p = 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;
+ target.setTask(p.task, p.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to bottom task " + p.task);
} else {
@@ -1721,7 +1719,8 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- target.task = new TaskRecord(mService.mCurTask, target.info, null);
+ target.setTask(new TaskRecord(mService.mCurTask, target.info, null),
+ null, false);
target.task.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
@@ -1731,16 +1730,16 @@ public class ActivityStack {
replyChainEnd = targetI;
}
int dstPos = 0;
+ ThumbnailHolder curThumbHolder = target.thumbHolder;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
+ p = 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++;
+ p.setTask(target.task, curThumbHolder, false);
+ curThumbHolder = p.thumbHolder;
mHistory.remove(srcPos);
mHistory.add(dstPos, p);
mService.mWindowManager.moveAppToken(dstPos, p);
@@ -1770,7 +1769,7 @@ public class ActivityStack {
// like these are all in the reply chain.
replyChainEnd = targetI+1;
while (replyChainEnd < mHistory.size() &&
- ((ActivityRecord)mHistory.get(
+ (mHistory.get(
replyChainEnd)).task == task) {
replyChainEnd++;
}
@@ -1780,7 +1779,7 @@ public class ActivityStack {
}
ActivityRecord p = null;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
+ p = mHistory.get(srcPos);
if (p.finishing) {
continue;
}
@@ -1840,7 +1839,7 @@ public class ActivityStack {
}
ActivityRecord p = null;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
+ p = mHistory.get(srcPos);
if (p.finishing) {
continue;
}
@@ -1858,7 +1857,7 @@ public class ActivityStack {
replyChainEnd = targetI;
}
for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
+ ActivityRecord p = mHistory.get(srcPos);
if (p.finishing) {
continue;
}
@@ -1869,12 +1868,10 @@ public class ActivityStack {
lastReparentPos--;
}
mHistory.remove(srcPos);
- p.task.numActivities--;
- p.task = task;
+ p.setTask(task, null, false);
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) {
@@ -1889,7 +1886,7 @@ public class ActivityStack {
// 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);
+ ActivityRecord p = mHistory.get(j);
if (p.finishing) {
continue;
}
@@ -1930,7 +1927,7 @@ public class ActivityStack {
// First find the requested task.
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.task.taskId == taskId) {
i++;
break;
@@ -1940,7 +1937,7 @@ public class ActivityStack {
// Now clear it.
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.finishing) {
continue;
}
@@ -1952,7 +1949,7 @@ public class ActivityStack {
ActivityRecord ret = r;
while (i < (mHistory.size()-1)) {
i++;
- r = (ActivityRecord)mHistory.get(i);
+ r = mHistory.get(i);
if (r.task.taskId != taskId) {
break;
}
@@ -1988,6 +1985,28 @@ public class ActivityStack {
}
/**
+ * Completely remove all activities associated with an existing
+ * task starting at a specified index.
+ */
+ private final void performClearTaskAtIndexLocked(int taskId, int i) {
+ while (i < (mHistory.size()-1)) {
+ ActivityRecord r = mHistory.get(i);
+ if (r.task.taskId != taskId) {
+ // Whoops hit the end.
+ return;
+ }
+ if (r.finishing) {
+ i++;
+ continue;
+ }
+ if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i++;
+ }
+ }
+ }
+
+ /**
* Completely remove all activities associated with an existing task.
*/
private final void performClearTaskLocked(int taskId) {
@@ -1996,37 +2015,23 @@ public class ActivityStack {
// First find the requested task.
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.task.taskId == taskId) {
i++;
break;
}
}
- // Now clear it.
+ // Now find the start and clear it.
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.finishing) {
continue;
}
if (r.task.taskId != taskId) {
// We hit the bottom. Now finish it all...
- while (i < (mHistory.size()-1)) {
- i++;
- r = (ActivityRecord)mHistory.get(i);
- if (r.task.taskId != taskId) {
- // Whoops hit the end.
- return;
- }
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- i--;
- }
- }
+ performClearTaskAtIndexLocked(taskId, i+1);
return;
}
}
@@ -2040,7 +2045,7 @@ public class ActivityStack {
int i = mHistory.size();
while (i > 0) {
i--;
- ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
+ ActivityRecord candidate = mHistory.get(i);
if (candidate.task.taskId != task) {
break;
}
@@ -2057,9 +2062,9 @@ public class ActivityStack {
* brought to the front.
*/
private final ActivityRecord moveActivityToFrontLocked(int where) {
- ActivityRecord newTop = (ActivityRecord)mHistory.remove(where);
+ ActivityRecord newTop = mHistory.remove(where);
int top = mHistory.size();
- ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
+ ActivityRecord oldTop = mHistory.get(top-1);
mHistory.add(top, newTop);
oldTop.frontOfTask = false;
newTop.frontOfTask = true;
@@ -2102,7 +2107,7 @@ public class ActivityStack {
if (DEBUG_RESULTS) Slog.v(
TAG, "Sending result to " + resultTo + " (index " + index + ")");
if (index >= 0) {
- sourceRecord = (ActivityRecord)mHistory.get(index);
+ sourceRecord = mHistory.get(index);
if (requestCode >= 0 && !sourceRecord.finishing) {
resultRecord = sourceRecord;
}
@@ -2527,11 +2532,11 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- r.task = new TaskRecord(mService.mCurTask, r.info, intent);
+ r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new task " + r.task);
} else {
- r.task = reuseTask;
+ r.setTask(reuseTask, reuseTask, true);
}
newTask = true;
moveHomeToFrontFromLaunchLocked(launchFlags);
@@ -2574,7 +2579,7 @@ public class ActivityStack {
// 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;
+ r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in existing task " + r.task);
@@ -2584,10 +2589,10 @@ public class ActivityStack {
// this case should never happen.
final int N = mHistory.size();
ActivityRecord prev =
- N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
- r.task = prev != null
+ N > 0 ? mHistory.get(N-1) : null;
+ r.setTask(prev != null
? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent);
+ : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new guessed " + r.task);
}
@@ -3029,7 +3034,7 @@ public class ActivityStack {
// Get the activity record.
int index = indexOfTokenLocked(token);
if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = mHistory.get(index);
if (fromTimeout) {
reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
@@ -3161,12 +3166,12 @@ public class ActivityStack {
if (index < 0) {
return false;
}
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = 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);
+ ActivityRecord p = mHistory.get(i);
if (!p.finishing && p != r) {
lastActivity = false;
break;
@@ -3201,7 +3206,7 @@ public class ActivityStack {
System.identityHashCode(r),
r.task.taskId, r.shortComponentName, reason);
if (index < (mHistory.size()-1)) {
- ActivityRecord next = (ActivityRecord)mHistory.get(index+1);
+ ActivityRecord next = mHistory.get(index+1);
if (next.task == r.task) {
if (r.frontOfTask) {
// The next activity is now the front of the task.
@@ -3257,7 +3262,7 @@ public class ActivityStack {
if (mResumedActivity == r) {
boolean endTask = index <= 0
- || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
+ || (mHistory.get(index-1)).task != r.task;
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare close transition: finishing " + r);
mService.mWindowManager.prepareAppTransition(endTask
@@ -3399,13 +3404,14 @@ public class ActivityStack {
// Get rid of any pending idle timeouts.
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+ mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
}
private final void removeActivityFromHistoryLocked(ActivityRecord r) {
if (r.state != ActivityState.DESTROYED) {
r.makeFinishing();
mHistory.remove(r);
- r.inHistory = false;
+ r.takeFromHistory();
r.state = ActivityState.DESTROYED;
mService.mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
@@ -3524,7 +3530,7 @@ public class ActivityStack {
int index = indexOfTokenLocked(token);
if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = mHistory.get(index);
if (r.state == ActivityState.DESTROYING) {
final long origId = Binder.clearCallingIdentity();
removeActivityFromHistoryLocked(r);
@@ -3566,7 +3572,7 @@ public class ActivityStack {
final void moveHomeToFrontLocked() {
TaskRecord homeTask = null;
for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+ ActivityRecord hr = mHistory.get(i);
if (hr.isHomeActivity) {
homeTask = hr.task;
break;
@@ -3584,7 +3590,7 @@ public class ActivityStack {
final int task = tr.taskId;
int top = mHistory.size()-1;
- if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
+ if (top < 0 || (mHistory.get(top)).task.taskId == task) {
// nothing to do!
return;
}
@@ -3599,7 +3605,7 @@ public class ActivityStack {
// 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);
+ ActivityRecord r = mHistory.get(pos);
if (localLOGV) Slog.v(
TAG, "At " + pos + " ckp " + r.task + ": " + r);
if (r.task.taskId == task) {
@@ -3688,7 +3694,7 @@ public class ActivityStack {
// 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);
+ ActivityRecord r = mHistory.get(pos);
if (localLOGV) Slog.v(
TAG, "At " + pos + " ckp " + r.task + ": " + r);
if (r.task.taskId == task) {
@@ -3722,6 +3728,106 @@ public class ActivityStack {
return true;
}
+ public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) {
+ TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true);
+ ActivityRecord resumed = mResumedActivity;
+ if (resumed != null && resumed.thumbHolder == tr) {
+ info.mainThumbnail = resumed.stack.screenshotActivities(resumed);
+ } else {
+ info.mainThumbnail = tr.lastThumbnail;
+ }
+ return info;
+ }
+
+ public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex) {
+ TaskAccessInfo info = getTaskAccessInfoLocked(taskId, false);
+ if (info.root == null) {
+ Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
+ return null;
+ }
+
+ if (subTaskIndex < 0) {
+ // Just remove the entire task.
+ performClearTaskAtIndexLocked(taskId, info.rootIndex);
+ return info.root;
+ }
+
+ if (subTaskIndex >= info.subtasks.size()) {
+ Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex);
+ return null;
+ }
+
+ // Remove all of this task's activies starting at the sub task.
+ TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex);
+ performClearTaskAtIndexLocked(taskId, subtask.index);
+ return subtask.activity;
+ }
+
+ public TaskAccessInfo getTaskAccessInfoLocked(int taskId, boolean inclThumbs) {
+ ActivityRecord resumed = mResumedActivity;
+ final TaskAccessInfo thumbs = new TaskAccessInfo();
+ // How many different sub-thumbnails?
+ final int NA = mHistory.size();
+ int j = 0;
+ ThumbnailHolder holder = null;
+ while (j < NA) {
+ ActivityRecord ar = mHistory.get(j);
+ if (!ar.finishing && ar.task.taskId == taskId) {
+ holder = ar.thumbHolder;
+ break;
+ }
+ j++;
+ }
+
+ if (j >= NA) {
+ return thumbs;
+ }
+
+ thumbs.root = mHistory.get(j);
+ thumbs.rootIndex = j;
+
+ ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>();
+ thumbs.subtasks = subtasks;
+ ActivityRecord lastActivity = null;
+ while (j < NA) {
+ ActivityRecord ar = mHistory.get(j);
+ j++;
+ if (ar.finishing) {
+ continue;
+ }
+ if (ar.task.taskId != taskId) {
+ break;
+ }
+ lastActivity = ar;
+ if (ar.thumbHolder != holder && holder != null) {
+ thumbs.numSubThumbbails++;
+ holder = ar.thumbHolder;
+ TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask();
+ sub.thumbnail = holder.lastThumbnail;
+ sub.activity = ar;
+ sub.index = j-1;
+ subtasks.add(sub);
+ }
+ }
+ if (lastActivity != null && subtasks.size() > 0) {
+ if (resumed == lastActivity) {
+ TaskAccessInfo.SubTask sub = subtasks.get(subtasks.size()-1);
+ sub.thumbnail = lastActivity.stack.screenshotActivities(lastActivity);
+ }
+ }
+ if (thumbs.numSubThumbbails > 0) {
+ thumbs.retriever = new IThumbnailRetriever.Stub() {
+ public Bitmap getThumbnail(int index) {
+ if (index < 0 || index >= thumbs.subtasks.size()) {
+ return null;
+ }
+ return thumbs.subtasks.get(index).thumbnail;
+ }
+ };
+ }
+ return thumbs;
+ }
+
private final void logStartActivity(int tag, ActivityRecord r,
TaskRecord task) {
EventLog.writeEvent(tag,
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 963a691..b4fdc9f 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -449,6 +449,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
boolean isCheckin = false;
+ boolean noOutput = false;
if (args != null) {
for (String arg : args) {
if ("--checkin".equals(arg)) {
@@ -457,10 +458,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
synchronized (mStats) {
mStats.resetAllStatsLocked();
pw.println("Battery stats reset.");
+ noOutput = true;
}
+ } else if ("--write".equals(arg)) {
+ synchronized (mStats) {
+ mStats.writeSyncLocked();
+ pw.println("Battery stats written.");
+ noOutput = true;
+ }
+ } else {
+ pw.println("Unknown option: " + arg);
}
}
}
+ if (noOutput) {
+ return;
+ }
if (isCheckin) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
synchronized (mStats) {
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 44c9742..db235ee 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -60,7 +60,7 @@ class ContentProviderRecord extends ContentProviderHolder {
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("package=");
pw.print(info.applicationInfo.packageName);
- pw.print("process="); pw.println(info.processName);
+ pw.print(" process="); pw.println(info.processName);
pw.print(prefix); pw.print("app="); pw.println(app);
if (launchingApp != null) {
pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index a63ffae..5be35ee 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -66,6 +66,7 @@ class ProcessRecord {
boolean foregroundServices; // Running any services that are foreground?
boolean bad; // True if disabled in the bad process list
boolean killedBackground; // True when proc has been killed due to too many bg
+ String waitingToKill; // Process is waiting to be killed when in the bg; reason
IBinder forcingToForeground;// Token that is forcing this process to be foreground
int adjSeq; // Sequence id for identifying oom_adj assignment cycles
int lruSeq; // Sequence id for identifying LRU update cycles
@@ -203,8 +204,9 @@ class ProcessRecord {
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);
+ if (killedBackground || waitingToKill != null) {
+ pw.print(prefix); pw.print("killedBackground="); pw.print(killedBackground);
+ pw.print(" waitingToKill="); pw.println(waitingToKill);
}
if (debugging || crashing || crashDialog != null || notResponding
|| anrDialog != null || bad) {
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index e5aceb4..004e963 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -84,7 +84,6 @@ class ServiceRecord extends Binder {
boolean startRequested; // someone explicitly called start?
boolean stopIfKilled; // last onStart() said to stop if service killed?
boolean callStart; // last onStart() has asked to alway be called on restart.
- int lastStartId; // identifier of most recent start request.
int executeNesting; // number of outstanding operations keeping foreground.
long executingStart; // start time of last execute request.
int crashCount; // number of times proc has crashed with service running
@@ -96,8 +95,11 @@ class ServiceRecord extends Binder {
String stringName; // caching of toString
+ private int lastStartId; // identifier of most recent start request.
+
static class StartItem {
final ServiceRecord sr;
+ final boolean taskRemoved;
final int id;
final Intent intent;
final int targetPermissionUid;
@@ -108,8 +110,10 @@ class ServiceRecord extends Binder {
String stringName; // caching of toString
- StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+ StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
+ int _targetPermissionUid) {
sr = _sr;
+ taskRemoved = _taskRemoved;
id = _id;
intent = _intent;
targetPermissionUid = _targetPermissionUid;
@@ -198,7 +202,9 @@ class ServiceRecord extends Binder {
long now = SystemClock.uptimeMillis();
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);
+ 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) {
@@ -211,7 +217,7 @@ class ServiceRecord extends Binder {
pw.print(" lastActivity=");
TimeUtils.formatDuration(lastActivity, now, pw);
pw.println("");
- pw.print(prefix); pw.print(" executingStart=");
+ pw.print(prefix); pw.print("executingStart=");
TimeUtils.formatDuration(executingStart, now, pw);
pw.print(" restartTime=");
TimeUtils.formatDuration(restartTime, now, pw);
@@ -321,6 +327,18 @@ class ServiceRecord extends Binder {
return null;
}
+ public int getLastStartId() {
+ return lastStartId;
+ }
+
+ public int makeNextStartId() {
+ lastStartId++;
+ if (lastStartId < 1) {
+ lastStartId = 1;
+ }
+ return lastStartId;
+ }
+
public void postNotification() {
final int appUid = appInfo.uid;
final int appPid = app.pid;
diff --git a/services/java/com/android/server/am/TaskAccessInfo.java b/services/java/com/android/server/am/TaskAccessInfo.java
new file mode 100644
index 0000000..5618c1a
--- /dev/null
+++ b/services/java/com/android/server/am/TaskAccessInfo.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import java.util.ArrayList;
+
+import android.app.ActivityManager.TaskThumbnails;
+import android.graphics.Bitmap;
+
+final class TaskAccessInfo extends TaskThumbnails {
+ final static class SubTask {
+ Bitmap thumbnail;
+ ActivityRecord activity;
+ int index;
+ }
+
+ public ActivityRecord root;
+ public int rootIndex;
+
+ public ArrayList<SubTask> subtasks;
+}
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 86cec42..e8c87e1 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -23,7 +23,7 @@ import android.graphics.Bitmap;
import java.io.PrintWriter;
-class TaskRecord {
+class TaskRecord extends ThumbnailHolder {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
Intent intent; // The original intent that started the task.
@@ -34,8 +34,6 @@ class TaskRecord {
long lastActiveTime; // Last time this task was active, including sleep.
boolean rootWasReset; // True if the intent at the root of the task had
// the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
- Bitmap lastThumbnail; // Last thumbnail captured for this task.
- CharSequence lastDescription; // Last description captured for this task.
String stringName; // caching of toString() result.
diff --git a/services/java/com/android/server/am/ThumbnailHolder.java b/services/java/com/android/server/am/ThumbnailHolder.java
new file mode 100644
index 0000000..02f4fcb
--- /dev/null
+++ b/services/java/com/android/server/am/ThumbnailHolder.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.graphics.Bitmap;
+
+public class ThumbnailHolder {
+ Bitmap lastThumbnail; // Last thumbnail captured for this item.
+ CharSequence lastDescription; // Last description captured for this item.
+}
diff --git a/services/java/com/android/server/am/TransferPipe.java b/services/java/com/android/server/am/TransferPipe.java
new file mode 100644
index 0000000..c3f4f93
--- /dev/null
+++ b/services/java/com/android/server/am/TransferPipe.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+
+/**
+ * Helper for transferring data through a pipe from a client app.
+ */
+class TransferPipe implements Runnable {
+ static final String TAG = "TransferPipe";
+ static final boolean DEBUG = false;
+
+ static final long DEFAULT_TIMEOUT = 5000; // 5 seconds
+
+ final Thread mThread;;
+ final ParcelFileDescriptor[] mFds;
+
+ FileDescriptor mOutFd;
+ long mEndTime;
+ String mFailure;
+ boolean mComplete;
+
+ String mBufferPrefix;
+
+ interface Caller {
+ void go(IInterface iface, FileDescriptor fd, String prefix,
+ String[] args) throws RemoteException;
+ }
+
+ TransferPipe() throws IOException {
+ mThread = new Thread(this, "TransferPipe");
+ mFds = ParcelFileDescriptor.createPipe();
+ }
+
+ ParcelFileDescriptor getReadFd() {
+ return mFds[0];
+ }
+
+ ParcelFileDescriptor getWriteFd() {
+ return mFds[1];
+ }
+
+ void setBufferPrefix(String prefix) {
+ mBufferPrefix = prefix;
+ }
+
+ static void go(Caller caller, IInterface iface, FileDescriptor out,
+ String prefix, String[] args) throws IOException, RemoteException {
+ go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT);
+ }
+
+ static void go(Caller caller, IInterface iface, FileDescriptor out,
+ String prefix, String[] args, long timeout) throws IOException, RemoteException {
+ if ((iface.asBinder()) instanceof Binder) {
+ // This is a local object... just call it directly.
+ try {
+ caller.go(iface, out, prefix, args);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+
+ TransferPipe tp = new TransferPipe();
+ try {
+ caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args);
+ tp.go(out, timeout);
+ } finally {
+ tp.kill();
+ }
+ }
+
+ static void goDump(IBinder binder, FileDescriptor out,
+ String[] args) throws IOException, RemoteException {
+ goDump(binder, out, args, DEFAULT_TIMEOUT);
+ }
+
+ static void goDump(IBinder binder, FileDescriptor out,
+ String[] args, long timeout) throws IOException, RemoteException {
+ if (binder instanceof Binder) {
+ // This is a local object... just call it directly.
+ try {
+ binder.dump(out, args);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+
+ TransferPipe tp = new TransferPipe();
+ try {
+ binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
+ tp.go(out, timeout);
+ } finally {
+ tp.kill();
+ }
+ }
+
+ void go(FileDescriptor out) throws IOException {
+ go(out, DEFAULT_TIMEOUT);
+ }
+
+ void go(FileDescriptor out, long timeout) throws IOException {
+ try {
+ synchronized (this) {
+ mOutFd = out;
+ mEndTime = SystemClock.uptimeMillis() + timeout;
+
+ if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd()
+ + " out=" + out);
+
+ // Close the write fd, so we know when the other side is done.
+ closeFd(1);
+
+ mThread.start();
+
+ while (mFailure == null && !mComplete) {
+ long waitTime = mEndTime - SystemClock.uptimeMillis();
+ if (waitTime <= 0) {
+ if (DEBUG) Slog.i(TAG, "TIMEOUT!");
+ mThread.interrupt();
+ throw new IOException("Timeout");
+ }
+
+ try {
+ wait(waitTime);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ if (DEBUG) Slog.i(TAG, "Finished: " + mFailure);
+ if (mFailure != null) {
+ throw new IOException(mFailure);
+ }
+ }
+ } finally {
+ kill();
+ }
+ }
+
+ void closeFd(int num) {
+ if (mFds[num] != null) {
+ if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]);
+ try {
+ mFds[num].close();
+ } catch (IOException e) {
+ }
+ mFds[num] = null;
+ }
+ }
+
+ void kill() {
+ closeFd(0);
+ closeFd(1);
+ }
+
+ @Override
+ public void run() {
+ final byte[] buffer = new byte[1024];
+ final FileInputStream fis = new FileInputStream(getReadFd().getFileDescriptor());
+ final FileOutputStream fos = new FileOutputStream(mOutFd);
+
+ if (DEBUG) Slog.i(TAG, "Ready to read pipe...");
+ byte[] bufferPrefix = null;
+ boolean needPrefix = true;
+ if (mBufferPrefix != null) {
+ bufferPrefix = mBufferPrefix.getBytes();
+ }
+
+ int size;
+ try {
+ while ((size=fis.read(buffer)) > 0) {
+ if (DEBUG) Slog.i(TAG, "Got " + size + " bytes");
+ if (bufferPrefix == null) {
+ fos.write(buffer, 0, size);
+ } else {
+ int start = 0;
+ for (int i=0; i<size; i++) {
+ if (buffer[i] != '\n') {
+ if (i > start) {
+ fos.write(buffer, start, i-start);
+ }
+ start = i;
+ if (needPrefix) {
+ fos.write(bufferPrefix);
+ needPrefix = false;
+ }
+ do {
+ i++;
+ } while (i<size && buffer[i] != '\n');
+ if (i < size) {
+ needPrefix = true;
+ }
+ }
+ }
+ if (size > start) {
+ fos.write(buffer, start, size-start);
+ }
+ }
+ }
+ if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size);
+ if (mThread.isInterrupted()) {
+ if (DEBUG) Slog.i(TAG, "Interrupted!");
+ }
+ } catch (IOException e) {
+ synchronized (this) {
+ mFailure = e.toString();
+ notifyAll();
+ return;
+ }
+ }
+
+ synchronized (this) {
+ mComplete = true;
+ notifyAll();
+ }
+ }
+}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 9ff5233..316db2e 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -84,6 +84,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private String[] mTetherableBluetoothRegexs;
private String[] mUpstreamIfaceRegexs;
+ private INetworkManagementService mNMService;
private Looper mLooper;
private HandlerThread mThread;
@@ -100,21 +101,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// with 255.255.255.0
private String[] mDhcpRange;
- private static final String DHCP_DEFAULT_RANGE1_START = "192.168.42.2";
- private static final String DHCP_DEFAULT_RANGE1_STOP = "192.168.42.254";
- private static final String DHCP_DEFAULT_RANGE2_START = "192.168.43.2";
- private static final String DHCP_DEFAULT_RANGE2_STOP = "192.168.43.254";
- private static final String DHCP_DEFAULT_RANGE3_START = "192.168.44.2";
- private static final String DHCP_DEFAULT_RANGE3_STOP = "192.168.44.254";
- private static final String DHCP_DEFAULT_RANGE4_START = "192.168.45.2";
- private static final String DHCP_DEFAULT_RANGE4_STOP = "192.168.45.254";
- private static final String DHCP_DEFAULT_RANGE5_START = "192.168.46.2";
- private static final String DHCP_DEFAULT_RANGE5_STOP = "192.168.46.254";
- private static final String DHCP_DEFAULT_RANGE6_START = "192.168.47.2";
- private static final String DHCP_DEFAULT_RANGE6_STOP = "192.168.47.254";
- private static final String DHCP_DEFAULT_RANGE7_START = "192.168.48.2";
- private static final String DHCP_DEFAULT_RANGE7_STOP = "192.168.48.254";
-
+ private static final String[] DHCP_DEFAULT_RANGE = {
+ "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
+ "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
+ "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
+ "192.168.48.2", "192.168.48.254",
+ };
private String[] mDnsServers;
private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
@@ -132,15 +124,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private boolean mUsbMassStorageOff; // track the status of USB Mass Storage
private boolean mUsbConnected; // track the status of USB connection
- public Tethering(Context context, Looper looper) {
+ public Tethering(Context context, INetworkManagementService nmService, Looper looper) {
mContext = context;
+ mNMService = nmService;
mLooper = looper;
// register for notifications from NetworkManagement Service
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
- service.registerObserver(this);
+ mNMService.registerObserver(this);
} catch (RemoteException e) {
Log.e(TAG, "Error registering observer :" + e);
}
@@ -173,21 +164,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mDhcpRange = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_dhcp_range);
if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
- mDhcpRange = new String[14];
- mDhcpRange[0] = DHCP_DEFAULT_RANGE1_START;
- mDhcpRange[1] = DHCP_DEFAULT_RANGE1_STOP;
- mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
- mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
- mDhcpRange[4] = DHCP_DEFAULT_RANGE3_START;
- mDhcpRange[5] = DHCP_DEFAULT_RANGE3_STOP;
- mDhcpRange[6] = DHCP_DEFAULT_RANGE4_START;
- mDhcpRange[7] = DHCP_DEFAULT_RANGE4_STOP;
- mDhcpRange[8] = DHCP_DEFAULT_RANGE5_START;
- mDhcpRange[9] = DHCP_DEFAULT_RANGE5_STOP;
- mDhcpRange[10] = DHCP_DEFAULT_RANGE6_START;
- mDhcpRange[11] = DHCP_DEFAULT_RANGE6_STOP;
- mDhcpRange[12] = DHCP_DEFAULT_RANGE7_START;
- mDhcpRange[13] = DHCP_DEFAULT_RANGE7_STOP;
+ mDhcpRange = DHCP_DEFAULT_RANGE;
}
mDunRequired = false; // resample when we turn on
@@ -258,8 +235,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return false;
}
public void interfaceAdded(String iface) {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
boolean found = false;
boolean usb = false;
if (isWifi(iface)) {
@@ -354,9 +329,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private void sendTetherStateChangedBroadcast() {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
try {
- if (!service.isTetheringSupported()) return;
+ if (!cm.isTetheringSupported()) return;
} catch (RemoteException e) {
return;
}
@@ -503,11 +478,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// used on cable insert/remove
private void enableUsbIfaces(boolean enable) {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
String[] ifaces = new String[0];
try {
- ifaces = service.listInterfaces();
+ ifaces = mNMService.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return;
@@ -526,19 +499,17 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// toggled when we enter/leave the fully tethered state
private boolean enableUsbRndis(boolean enabled) {
if (DEBUG) Log.d(TAG, "enableUsbRndis(" + enabled + ")");
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
if (enabled) {
synchronized (this) {
- if (!service.isUsbRNDISStarted()) {
- service.startUsbRNDIS();
+ if (!mNMService.isUsbRNDISStarted()) {
+ mNMService.startUsbRNDIS();
}
}
} else {
- if (service.isUsbRNDISStarted()) {
- service.stopUsbRNDIS();
+ if (mNMService.isUsbRNDISStarted()) {
+ mNMService.stopUsbRNDIS();
}
}
} catch (Exception e) {
@@ -552,13 +523,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private boolean configureUsbIface(boolean enabled) {
if (DEBUG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
// bring toggle the interfaces
String[] ifaces = new String[0];
try {
- ifaces = service.listInterfaces();
+ ifaces = mNMService.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return false;
@@ -567,7 +535,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
if (isUsb(iface)) {
InterfaceConfiguration ifcg = null;
try {
- ifcg = service.getInterfaceConfig(iface);
+ ifcg = mNMService.getInterfaceConfig(iface);
if (ifcg != null) {
InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH);
@@ -578,7 +546,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
- service.setInterfaceConfig(iface, ifcg);
+ mNMService.setInterfaceConfig(iface, ifcg);
}
} catch (Exception e) {
Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
@@ -874,11 +842,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
class TetheredState extends State {
@Override
public void enter() {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.tetherInterface(mIfaceName);
+ mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
@@ -903,16 +868,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
switch (message.what) {
case CMD_TETHER_UNREQUESTED:
case CMD_INTERFACE_DOWN:
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
if (mMyUpstreamIfaceName != null) {
try {
- service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastErrorAndTransitionToInitialState(
@@ -921,7 +883,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
@@ -944,8 +906,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
break;
case CMD_TETHER_CONNECTION_CHANGED:
String newUpstreamIfaceName = (String)(message.obj);
- b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- service = INetworkManagementService.Stub.asInterface(b);
if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
(mMyUpstreamIfaceName != null &&
mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
@@ -954,11 +914,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
if (mMyUpstreamIfaceName != null) {
try {
- service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastErrorAndTransitionToInitialState(
@@ -968,10 +928,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
if (newUpstreamIfaceName != null) {
try {
- service.enableNat(mIfaceName, newUpstreamIfaceName);
+ mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
} catch (Exception e) {
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
@@ -990,15 +950,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
error = true;
// fall through
case CMD_TETHER_MODE_DEAD:
- b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- service = INetworkManagementService.Stub.asInterface(b);
if (mMyUpstreamIfaceName != null) {
try {
- service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastErrorAndTransitionToInitialState(
@@ -1007,7 +965,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
@@ -1138,10 +1096,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
boolean retValue = true;
if (mMobileReserved) return retValue;
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
int result = Phone.APN_REQUEST_FAILED;
try {
- result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ result = cm.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
(mDunRequired ? Phone.FEATURE_ENABLE_DUN_ALWAYS :
Phone.FEATURE_ENABLE_HIPRI), new Binder());
} catch (Exception e) {
@@ -1165,10 +1123,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
protected boolean turnOffMobileConnection() {
if (mMobileReserved) {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service =
- IConnectivityManager.Stub.asInterface(b);
+ IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
try {
- service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ cm.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
(mDunRequired? Phone.FEATURE_ENABLE_DUN_ALWAYS :
Phone.FEATURE_ENABLE_HIPRI));
} catch (Exception e) {
@@ -1179,28 +1136,25 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return true;
}
protected boolean turnOnMasterTetherSettings() {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.setIpForwardingEnabled(true);
+ mNMService.setIpForwardingEnabled(true);
} catch (Exception e) {
transitionTo(mSetIpForwardingEnabledErrorState);
return false;
}
try {
- service.startTethering(mDhcpRange);
+ mNMService.startTethering(mDhcpRange);
} catch (Exception e) {
try {
- service.stopTethering();
- service.startTethering(mDhcpRange);
+ mNMService.stopTethering();
+ mNMService.startTethering(mDhcpRange);
} catch (Exception ee) {
transitionTo(mStartTetheringErrorState);
return false;
}
}
try {
- service.setDnsForwarders(mDnsServers);
+ mNMService.setDnsForwarders(mDnsServers);
} catch (Exception e) {
transitionTo(mSetDnsForwardersErrorState);
return false;
@@ -1208,17 +1162,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return true;
}
protected boolean turnOffMasterTetherSettings() {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.stopTethering();
+ mNMService.stopTethering();
} catch (Exception e) {
transitionTo(mStopTetheringErrorState);
return false;
}
try {
- service.setIpForwardingEnabled(false);
+ mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {
transitionTo(mSetIpForwardingDisabledErrorState);
return false;
@@ -1241,12 +1192,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
} catch (RemoteException e) { }
- b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
String[] ifaces = new String[0];
try {
- ifaces = service.listInterfaces();
+ ifaces = mNMService.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return null;
@@ -1258,7 +1206,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// verify it is active
InterfaceConfiguration ifcg = null;
try {
- ifcg = service.getInterfaceConfig(iface);
+ ifcg = mNMService.getInterfaceConfig(iface);
if (ifcg.isActive()) {
return iface;
}
@@ -1486,11 +1434,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void enter() {
Log.e(TAG, "Error in startTethering");
notify(TetherInterfaceSM.CMD_START_TETHERING_ERROR);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.setIpForwardingEnabled(false);
+ mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
@@ -1500,11 +1445,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void enter() {
Log.e(TAG, "Error in stopTethering");
notify(TetherInterfaceSM.CMD_STOP_TETHERING_ERROR);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.setIpForwardingEnabled(false);
+ mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
@@ -1514,14 +1456,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void enter() {
Log.e(TAG, "Error in setDnsForwarders");
notify(TetherInterfaceSM.CMD_SET_DNS_FORWARDERS_ERROR);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.stopTethering();
+ mNMService.stopTethering();
} catch (Exception e) {}
try {
- service.setIpForwardingEnabled(false);
+ mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
diff --git a/services/java/com/android/server/location/ComprehensiveCountryDetector.java b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
index e9ce3ce..bb9e60f 100755
--- a/services/java/com/android/server/location/ComprehensiveCountryDetector.java
+++ b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
@@ -56,6 +56,7 @@ import java.util.TimerTask;
public class ComprehensiveCountryDetector extends CountryDetectorBase {
private final static String TAG = "ComprehensiveCountryDetector";
+ /* package */ static final boolean DEBUG = false;
/**
* The refresh interval when the location based country was used
@@ -90,7 +91,9 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
* The listener for receiving the notification from LocationBasedCountryDetector.
*/
private CountryListener mLocationBasedCountryDetectionListener = new CountryListener() {
+ @Override
public void onCountryDetected(Country country) {
+ if (DEBUG) Slog.d(TAG, "Country detected via LocationBasedCountryDetector");
mCountryFromLocation = country;
// Don't start the LocationBasedCountryDetector.
detectCountry(true, false);
@@ -206,6 +209,7 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
protected void runAfterDetectionAsync(final Country country, final Country detectedCountry,
final boolean notifyChange, final boolean startLocationBasedDetection) {
mHandler.post(new Runnable() {
+ @Override
public void run() {
runAfterDetection(
country, detectedCountry, notifyChange, startLocationBasedDetection);
@@ -233,9 +237,20 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
if (notifyChange) {
notifyIfCountryChanged(country, detectedCountry);
}
+ if (DEBUG) {
+ Slog.d(TAG, "startLocationBasedDetection=" + startLocationBasedDetection
+ + " detectCountry=" + (detectedCountry == null ? null :
+ "(source: " + detectedCountry.getSource()
+ + ", countryISO: " + detectedCountry.getCountryIso() + ")")
+ + " isAirplaneModeOff()=" + isAirplaneModeOff()
+ + " mListener=" + mListener
+ + " isGeoCoderImplemnted()=" + isGeoCoderImplemented());
+ }
+
if (startLocationBasedDetection && (detectedCountry == null
|| detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION)
&& isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) {
+ if (DEBUG) Slog.d(TAG, "run startLocationBasedDetector()");
// Start finding location when the source is less reliable than the
// location and the airplane mode is off (as geocoder will not
// work).
@@ -266,12 +281,20 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
if (mLocationBasedCountryDetector != null) {
return;
}
+ if (DEBUG) {
+ Slog.d(TAG, "starts LocationBasedDetector to detect Country code via Location info "
+ + "(e.g. GPS)");
+ }
mLocationBasedCountryDetector = createLocationBasedCountryDetector();
mLocationBasedCountryDetector.setCountryListener(listener);
mLocationBasedCountryDetector.detectCountry();
}
private synchronized void stopLocationBasedDetector() {
+ if (DEBUG) {
+ Slog.d(TAG, "tries to stop LocationBasedDetector "
+ + "(current detector: " + mLocationBasedCountryDetector + ")");
+ }
if (mLocationBasedCountryDetector != null) {
mLocationBasedCountryDetector.stop();
mLocationBasedCountryDetector = null;
@@ -305,10 +328,17 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
*/
private synchronized void scheduleLocationRefresh() {
if (mLocationRefreshTimer != null) return;
+ if (DEBUG) {
+ Slog.d(TAG, "start periodic location refresh timer. Interval: "
+ + LOCATION_REFRESH_INTERVAL);
+ }
mLocationRefreshTimer = new Timer();
mLocationRefreshTimer.schedule(new TimerTask() {
@Override
public void run() {
+ if (DEBUG) {
+ Slog.d(TAG, "periodic location refresh event. Starts detecting Country code");
+ }
mLocationRefreshTimer = null;
detectCountry(false, true);
}
diff --git a/services/java/com/android/server/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
index 813d255..e420073 100644
--- a/services/java/com/android/server/location/GpsXtraDownloader.java
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -19,7 +19,6 @@ 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;
diff --git a/services/java/com/android/server/location/LocationBasedCountryDetector.java b/services/java/com/android/server/location/LocationBasedCountryDetector.java
index 139f05d..d4fb8ee 100755
--- a/services/java/com/android/server/location/LocationBasedCountryDetector.java
+++ b/services/java/com/android/server/location/LocationBasedCountryDetector.java
@@ -16,12 +16,6 @@
package com.android.server.location;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-
import android.content.Context;
import android.location.Address;
import android.location.Country;
@@ -32,6 +26,12 @@ import android.location.LocationManager;
import android.os.Bundle;
import android.util.Slog;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
/**
* This class detects which country the user currently is in through the enabled
* location providers and the GeoCoder
@@ -86,24 +86,23 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
return country;
}
+ protected boolean isAcceptableProvider(String provider) {
+ // We don't want to actively initiate a location fix here (with gps or network providers).
+ return LocationManager.PASSIVE_PROVIDER.equals(provider);
+ }
+
/**
- * Register the listeners with the location providers
+ * Register a listener with a provider name
*/
- protected void registerEnabledProviders(List<LocationListener> listeners) {
- int total = listeners.size();
- for (int i = 0; i< total; i++) {
- mLocationManager.requestLocationUpdates(
- mEnabledProviders.get(i), 0, 0, listeners.get(i));
- }
+ protected void registerListener(String provider, LocationListener listener) {
+ mLocationManager.requestLocationUpdates(provider, 0, 0, listener);
}
/**
- * Unregister the listeners with the location providers
+ * Unregister an already registered listener
*/
- protected void unregisterProviders(List<LocationListener> listeners) {
- for (LocationListener listener : listeners) {
- mLocationManager.removeUpdates(listener);
- }
+ protected void unregisterListener(LocationListener listener) {
+ mLocationManager.removeUpdates(listener);
}
/**
@@ -130,14 +129,11 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
return QUERY_LOCATION_TIMEOUT;
}
- /**
- * @return the total number of enabled location providers
- */
- protected int getTotalEnabledProviders() {
+ protected List<String> getEnabledProviders() {
if (mEnabledProviders == null) {
mEnabledProviders = mLocationManager.getProviders(true);
}
- return mEnabledProviders.size();
+ return mEnabledProviders;
}
/**
@@ -152,27 +148,36 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
throw new IllegalStateException();
}
// Request the location from all enabled providers.
- int totalProviders = getTotalEnabledProviders();
+ List<String> enabledProviders = getEnabledProviders();
+ int totalProviders = enabledProviders.size();
if (totalProviders > 0) {
mLocationListeners = new ArrayList<LocationListener>(totalProviders);
for (int i = 0; i < totalProviders; i++) {
- LocationListener listener = new LocationListener () {
- public void onLocationChanged(Location location) {
- if (location != null) {
- LocationBasedCountryDetector.this.stop();
- queryCountryCode(location);
+ String provider = enabledProviders.get(i);
+ if (isAcceptableProvider(provider)) {
+ LocationListener listener = new LocationListener () {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (location != null) {
+ LocationBasedCountryDetector.this.stop();
+ queryCountryCode(location);
+ }
}
- }
- public void onProviderDisabled(String provider) {
- }
- public void onProviderEnabled(String provider) {
- }
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
- };
- mLocationListeners.add(listener);
+ @Override
+ public void onProviderDisabled(String provider) {
+ }
+ @Override
+ public void onProviderEnabled(String provider) {
+ }
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+ mLocationListeners.add(listener);
+ registerListener(provider, listener);
+ }
}
- registerEnabledProviders(mLocationListeners);
+
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
@@ -197,7 +202,9 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
@Override
public synchronized void stop() {
if (mLocationListeners != null) {
- unregisterProviders(mLocationListeners);
+ for (LocationListener listener : mLocationListeners) {
+ unregisterListener(listener);
+ }
mLocationListeners = null;
}
if (mTimer != null) {
@@ -216,6 +223,7 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
}
if (mQueryThread != null) return;
mQueryThread = new Thread(new Runnable() {
+ @Override
public void run() {
String countryIso = null;
if (location != null) {
diff --git a/services/java/com/android/server/pm/BasePermission.java b/services/java/com/android/server/pm/BasePermission.java
new file mode 100644
index 0000000..4f27408
--- /dev/null
+++ b/services/java/com/android/server/pm/BasePermission.java
@@ -0,0 +1,59 @@
+/*
+ * 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.pm;
+
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionInfo;
+
+final class BasePermission {
+ final static int TYPE_NORMAL = 0;
+
+ final static int TYPE_BUILTIN = 1;
+
+ final static int TYPE_DYNAMIC = 2;
+
+ final String name;
+
+ String sourcePackage;
+
+ PackageSettingBase packageSetting;
+
+ final int type;
+
+ int protectionLevel;
+
+ PackageParser.Permission perm;
+
+ PermissionInfo pendingInfo;
+
+ int uid;
+
+ int[] gids;
+
+ BasePermission(String _name, String _sourcePackage, int _type) {
+ name = _name;
+ sourcePackage = _sourcePackage;
+ type = _type;
+ // Default to most conservative protection level.
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ }
+
+ public String toString() {
+ return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+ + "}";
+ }
+}
diff --git a/services/java/com/android/server/pm/GrantedPermissions.java b/services/java/com/android/server/pm/GrantedPermissions.java
new file mode 100644
index 0000000..c7629b9
--- /dev/null
+++ b/services/java/com/android/server/pm/GrantedPermissions.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.ApplicationInfo;
+
+import java.util.HashSet;
+
+class GrantedPermissions {
+ int pkgFlags;
+
+ HashSet<String> grantedPermissions = new HashSet<String>();
+
+ int[] gids;
+
+ GrantedPermissions(int pkgFlags) {
+ setFlags(pkgFlags);
+ }
+
+ @SuppressWarnings("unchecked")
+ GrantedPermissions(GrantedPermissions base) {
+ pkgFlags = base.pkgFlags;
+ grantedPermissions = (HashSet<String>) base.grantedPermissions.clone();
+
+ if (base.gids != null) {
+ gids = base.gids.clone();
+ }
+ }
+
+ void setFlags(int pkgFlags) {
+ this.pkgFlags = pkgFlags
+ & (ApplicationInfo.FLAG_SYSTEM
+ | ApplicationInfo.FLAG_FORWARD_LOCK
+ | ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+ }
+}
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/pm/Installer.java
index 08d1b82..d10aa97 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -14,28 +14,31 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.pm;
import android.content.pm.PackageStats;
-import android.net.LocalSocketAddress;
import android.net.LocalSocket;
-import android.util.Config;
+import android.net.LocalSocketAddress;
import android.util.Slog;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.Socket;
-
class Installer {
private static final String TAG = "Installer";
- InputStream mIn;
- OutputStream mOut;
- LocalSocket mSocket;
- byte buf[] = new byte[1024];
- int buflen = 0;
+ private static final boolean LOCAL_DEBUG = false;
+
+ InputStream mIn;
+
+ OutputStream mOut;
+
+ LocalSocket mSocket;
+
+ byte buf[] = new byte[1024];
+
+ int buflen = 0;
private boolean connect() {
if (mSocket != null) {
@@ -45,8 +48,8 @@ class Installer {
try {
mSocket = new LocalSocket();
- LocalSocketAddress address = new LocalSocketAddress(
- "installd", LocalSocketAddress.Namespace.RESERVED);
+ LocalSocketAddress address = new LocalSocketAddress("installd",
+ LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
@@ -59,112 +62,131 @@ class Installer {
return true;
}
- private void disconnect() {
- Slog.i(TAG,"disconnecting...");
- try {
- if (mSocket != null) mSocket.close();
- } catch (IOException ex) { }
- try {
- if (mIn != null) mIn.close();
- } catch (IOException ex) { }
- try {
- if (mOut != null) mOut.close();
- } catch (IOException ex) { }
- mSocket = null;
- mIn = null;
- mOut = null;
- }
-
- private boolean readBytes(byte buffer[], int len) {
- int off = 0, count;
- if (len < 0) return false;
- while (off != len) {
- try {
- count = mIn.read(buffer, off, len - off);
- if (count <= 0) {
+ private void disconnect() {
+ Slog.i(TAG, "disconnecting...");
+ try {
+ if (mSocket != null)
+ mSocket.close();
+ } catch (IOException ex) {
+ }
+ try {
+ if (mIn != null)
+ mIn.close();
+ } catch (IOException ex) {
+ }
+ try {
+ if (mOut != null)
+ mOut.close();
+ } catch (IOException ex) {
+ }
+ mSocket = null;
+ mIn = null;
+ mOut = null;
+ }
+
+ private boolean readBytes(byte buffer[], int len) {
+ int off = 0, count;
+ if (len < 0)
+ return false;
+ while (off != len) {
+ try {
+ count = mIn.read(buffer, off, len - off);
+ if (count <= 0) {
Slog.e(TAG, "read error " + count);
break;
}
- off += count;
- } catch (IOException ex) {
- Slog.e(TAG,"read exception");
- break;
- }
- }
-// Slog.i(TAG, "read "+len+" bytes");
- if (off == len) return true;
- disconnect();
- return false;
- }
-
- private boolean readReply() {
- int len;
- buflen = 0;
- if (!readBytes(buf, 2)) return false;
- len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
- if ((len < 1) || (len > 1024)) {
- Slog.e(TAG,"invalid reply length ("+len+")");
- disconnect();
- return false;
- }
- if (!readBytes(buf, len)) return false;
- buflen = len;
- return true;
- }
-
- private boolean writeCommand(String _cmd) {
- byte[] cmd = _cmd.getBytes();
- int len = cmd.length;
- if ((len < 1) || (len > 1024)) return false;
- buf[0] = (byte) (len & 0xff);
- buf[1] = (byte) ((len >> 8) & 0xff);
- try {
- mOut.write(buf, 0, 2);
- mOut.write(cmd, 0, len);
- } catch (IOException ex) {
- Slog.e(TAG,"write error");
- disconnect();
- return false;
- }
- return true;
- }
-
- private synchronized String transaction(String cmd) {
- if (!connect()) {
+ off += count;
+ } catch (IOException ex) {
+ Slog.e(TAG, "read exception");
+ break;
+ }
+ }
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "read " + len + " bytes");
+ }
+ if (off == len)
+ return true;
+ disconnect();
+ return false;
+ }
+
+ private boolean readReply() {
+ int len;
+ buflen = 0;
+ if (!readBytes(buf, 2))
+ return false;
+ len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+ if ((len < 1) || (len > 1024)) {
+ Slog.e(TAG, "invalid reply length (" + len + ")");
+ disconnect();
+ return false;
+ }
+ if (!readBytes(buf, len))
+ return false;
+ buflen = len;
+ return true;
+ }
+
+ private boolean writeCommand(String _cmd) {
+ byte[] cmd = _cmd.getBytes();
+ int len = cmd.length;
+ if ((len < 1) || (len > 1024))
+ return false;
+ buf[0] = (byte) (len & 0xff);
+ buf[1] = (byte) ((len >> 8) & 0xff);
+ try {
+ mOut.write(buf, 0, 2);
+ mOut.write(cmd, 0, len);
+ } catch (IOException ex) {
+ Slog.e(TAG, "write error");
+ disconnect();
+ return false;
+ }
+ return true;
+ }
+
+ private synchronized String transaction(String cmd) {
+ if (!connect()) {
Slog.e(TAG, "connection failed");
return "-1";
}
if (!writeCommand(cmd)) {
- /* If installd died and restarted in the background
- * (unlikely but possible) we'll fail on the next
- * write (this one). Try to reconnect and write
- * the command one more time before giving up.
- */
+ /*
+ * If installd died and restarted in the background (unlikely but
+ * possible) we'll fail on the next write (this one). Try to
+ * reconnect and write the command one more time before giving up.
+ */
Slog.e(TAG, "write command failed? reconnect!");
if (!connect() || !writeCommand(cmd)) {
return "-1";
}
}
-// Slog.i(TAG,"send: '"+cmd+"'");
- if (readReply()) {
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "send: '" + cmd + "'");
+ }
+ if (readReply()) {
String s = new String(buf, 0, buflen);
-// Slog.i(TAG,"recv: '"+s+"'");
- return s;
- } else {
-// Slog.i(TAG,"fail");
- return "-1";
- }
- }
-
- private int execute(String cmd) {
- String res = transaction(cmd);
- try {
- return Integer.parseInt(res);
- } catch (NumberFormatException ex) {
- return -1;
- }
- }
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "recv: '" + s + "'");
+ }
+ return s;
+ } else {
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "fail");
+ }
+ return "-1";
+ }
+ }
+
+ private int execute(String cmd) {
+ String res = transaction(cmd);
+ try {
+ return Integer.parseInt(res);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
public int install(String name, int uid, int gid) {
StringBuilder builder = new StringBuilder("install");
@@ -203,10 +225,12 @@ class Installer {
return execute(builder.toString());
}
- public int remove(String name) {
+ public int remove(String name, int userId) {
StringBuilder builder = new StringBuilder("remove");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ builder.append(userId);
return execute(builder.toString());
}
@@ -225,14 +249,34 @@ class Installer {
builder.append(name);
return execute(builder.toString());
}
-
- public int clearUserData(String name) {
+
+ public int createUserData(String name, int uid, int userId) {
+ StringBuilder builder = new StringBuilder("mkuserdata");
+ builder.append(' ');
+ builder.append(name);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(' ');
+ builder.append(userId);
+ return execute(builder.toString());
+ }
+
+ public int removeUserDataDirs(int userId) {
+ StringBuilder builder = new StringBuilder("rmuser");
+ builder.append(' ');
+ builder.append(userId);
+ return execute(builder.toString());
+ }
+
+ public int clearUserData(String name, int userId) {
StringBuilder builder = new StringBuilder("rmuserdata");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ builder.append(userId);
return execute(builder.toString());
}
-
+
public boolean ping() {
if (execute("ping") < 0) {
return false;
@@ -240,7 +284,7 @@ class Installer {
return true;
}
}
-
+
public int freeCache(long freeStorageSize) {
StringBuilder builder = new StringBuilder("freecache");
builder.append(' ');
@@ -250,8 +294,8 @@ class Installer {
/*
* @param packagePathSuffix The name of the path relative to install
- * directory. Say if the path name is /data/app/com.test-1.apk,
- * the package suffix path will be com.test-1
+ * directory. Say if the path name is /data/app/com.test-1.apk, the package
+ * suffix path will be com.test-1
*/
public int setForwardLockPerm(String packagePathSuffix, int gid) {
StringBuilder builder = new StringBuilder("protect");
@@ -261,7 +305,7 @@ class Installer {
builder.append(gid);
return execute(builder.toString());
}
-
+
public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath,
PackageStats pStats) {
StringBuilder builder = new StringBuilder("getsize");
@@ -275,7 +319,7 @@ class Installer {
String s = transaction(builder.toString());
String res[] = s.split(" ");
- if((res == null) || (res.length != 4)) {
+ if ((res == null) || (res.length != 4)) {
return -1;
}
try {
@@ -286,7 +330,7 @@ class Installer {
} catch (NumberFormatException e) {
return -1;
}
- }
+ }
public int moveFiles() {
return execute("movefiles");
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 4a66a40..de439ca 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -14,26 +14,30 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
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;
import com.android.internal.util.XmlUtils;
+import com.android.server.DeviceStorageMonitorService;
+import com.android.server.EventLogTags;
+import com.android.server.IntentResolver;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
-import android.content.Context;
import android.content.ComponentName;
+import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
@@ -42,7 +46,6 @@ import android.content.ServiceConnection;
import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -54,43 +57,45 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
-import android.content.pm.PackageStats;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import android.content.pm.PackageParser;
-import android.content.pm.PermissionInfo;
+import android.content.pm.PackageStats;
import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Debug;
+import android.os.Environment;
+import android.os.FileObserver;
+import android.os.FileUtils;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
-import android.os.RemoteException;
-import android.os.Environment;
-import android.os.FileObserver;
-import android.os.FileUtils;
-import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.security.SystemKeyStore;
-import android.util.*;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.LogPrinter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
import android.view.Display;
import android.view.WindowManager;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -129,25 +134,30 @@ import java.util.zip.ZipOutputStream;
mmm frameworks/base/tests/AndroidTests
adb install -r -f out/target/product/passion/data/app/AndroidTests.apk
adb shell am instrument -w -e class com.android.unit_tests.PackageManagerTests com.android.unit_tests/android.test.InstrumentationTestRunner
- *
+ *
+ * {@hide}
*/
-class PackageManagerService extends IPackageManager.Stub {
- private static final String TAG = "PackageManager";
- private static final boolean DEBUG_SETTINGS = false;
+public class PackageManagerService extends IPackageManager.Stub {
+ static final String TAG = "PackageManager";
+ static final boolean DEBUG_SETTINGS = false;
private static final boolean DEBUG_PREFERRED = false;
- private static final boolean DEBUG_UPGRADE = false;
+ static final boolean DEBUG_UPGRADE = false;
private static final boolean DEBUG_INSTALL = false;
- private static final boolean DEBUG_STOPPED = false;
-
- private static final boolean MULTIPLE_APPLICATION_UIDS = true;
+ private static final boolean DEBUG_REMOVE = false;
+ private static final boolean DEBUG_SHOW_INFO = false;
+ private static final boolean DEBUG_PACKAGE_INFO = false;
+ private static final boolean DEBUG_INTENT_MATCHING = false;
+ private static final boolean DEBUG_PACKAGE_SCANNING = false;
+ private static final boolean DEBUG_APP_DIR_OBSERVER = false;
+
+ static final boolean MULTIPLE_APPLICATION_UIDS = true;
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
- private static final int FIRST_APPLICATION_UID =
+ private static final int KEYCHAIN_UID = Process.KEYCHAIN_UID;
+ static final int FIRST_APPLICATION_UID =
Process.FIRST_APPLICATION_UID;
- private static final int MAX_APPLICATION_UIDS = 1000;
-
- private static final boolean SHOW_INFO = false;
+ static final int MAX_APPLICATION_UIDS = 1000;
private static final boolean GET_CERTIFICATES = true;
@@ -161,19 +171,6 @@ class PackageManagerService extends IPackageManager.Stub {
// package apks to install directory.
private static final String INSTALL_PACKAGE_SUFFIX = "-";
- /**
- * Indicates the state of installation. Used by PackageManager to
- * figure out incomplete installations. Say a package is being installed
- * (the state is set to PKG_INSTALL_INCOMPLETE) and remains so till
- * the package installation is successful or unsuccesful lin which case
- * the PackageManager will no longer maintain state information associated
- * with the package. If some exception(like device freeze or battery being
- * pulled out) occurs during installation of a package, the PackageManager
- * needs this information to clean up the previously failed installation.
- */
- private static final int PKG_INSTALL_INCOMPLETE = 0;
- private static final int PKG_INSTALL_COMPLETE = 1;
-
static final int SCAN_MONITOR = 1<<0;
static final int SCAN_NO_DEX = 1<<1;
static final int SCAN_FORCE_DEX = 1<<2;
@@ -212,6 +209,9 @@ class PackageManagerService extends IPackageManager.Stub {
// This is where all application persistent data goes.
final File mAppDataDir;
+ // This is where all application persistent data goes for secondary users.
+ final File mUserAppDataDir;
+
// This is the object monitoring the framework dir.
final FileObserver mFrameworkInstallObserver;
@@ -362,6 +362,9 @@ class PackageManagerService extends IPackageManager.Stub {
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
+
+ final UserManager mUserManager;
+
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
@@ -526,12 +529,12 @@ class PackageManagerService extends IPackageManager.Stub {
}
case MCS_GIVE_UP: {
if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_giveup too many retries");
- HandlerParams params = mPendingInstalls.remove(0);
+ mPendingInstalls.remove(0);
break;
}
case SEND_PENDING_BROADCAST : {
String packages[];
- ArrayList components[];
+ ArrayList<String> components[];
int size = 0;
int uids[];
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
@@ -563,8 +566,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Send broadcasts
for (int i = 0; i < size; i++) {
- sendPackageChangedBroadcast(packages[i], true,
- (ArrayList<String>)components[i], uids[i]);
+ sendPackageChangedBroadcast(packages[i], true, components[i], uids[i]);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
break;
@@ -662,7 +664,7 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
removeMessages(WRITE_SETTINGS);
removeMessages(WRITE_STOPPED_PACKAGES);
- mSettings.writeLP();
+ mSettings.writeLPr();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
@@ -670,7 +672,7 @@ class PackageManagerService extends IPackageManager.Stub {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mPackages) {
removeMessages(WRITE_STOPPED_PACKAGES);
- mSettings.writeStoppedLP();
+ mSettings.writeStoppedLPr();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
@@ -742,20 +744,24 @@ class PackageManagerService extends IPackageManager.Stub {
mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings();
- mSettings.addSharedUserLP("android.uid.system",
+ mSettings.addSharedUserLPw("android.uid.system",
Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLP("android.uid.phone",
+ mSettings.addSharedUserLPw("android.uid.phone",
MULTIPLE_APPLICATION_UIDS
? RADIO_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLP("android.uid.log",
+ mSettings.addSharedUserLPw("android.uid.log",
MULTIPLE_APPLICATION_UIDS
? LOG_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLP("android.uid.nfc",
+ mSettings.addSharedUserLPw("android.uid.nfc",
MULTIPLE_APPLICATION_UIDS
? NFC_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
+ mSettings.addSharedUserLPw("android.uid.keychain",
+ MULTIPLE_APPLICATION_UIDS
+ ? KEYCHAIN_UID : FIRST_APPLICATION_UID,
+ ApplicationInfo.FLAG_SYSTEM);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -790,14 +796,18 @@ class PackageManagerService extends IPackageManager.Stub {
d.getMetrics(mMetrics);
synchronized (mInstallLock) {
+ // writer
synchronized (mPackages) {
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
+ mUserManager = new UserManager(mInstaller, mUserAppDataDir);
+
if (mInstaller == null) {
// Make sure these dirs exist, when we are running in
// the simulator.
@@ -805,12 +815,13 @@ class PackageManagerService extends IPackageManager.Stub {
File miscDir = new File(dataDir, "misc");
miscDir.mkdirs();
mAppDataDir.mkdirs();
+ mUserAppDataDir.mkdirs();
mDrmAppPrivateInstallDir.mkdirs();
}
readPermissions();
- mRestoredSettings = mSettings.readLP();
+ mRestoredSettings = mSettings.readLPw();
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
@@ -973,7 +984,8 @@ class PackageManagerService extends IPackageManager.Stub {
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
if (mInstaller != null) {
- mInstaller.remove(ps.name);
+ mInstaller.remove(ps.name, 0);
+ mUserManager.removePackageForAllUsers(ps.name);
}
}
}
@@ -985,7 +997,7 @@ class PackageManagerService extends IPackageManager.Stub {
mAppInstallDir.mkdirs(); // scanDirLI() assumes this dir exists
}
//look for any incomplete package installations
- ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackages();
+ ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
//clean up list
for(int i = 0; i < deletePkgsList.size(); i++) {
//clean up here
@@ -1026,9 +1038,10 @@ class PackageManagerService extends IPackageManager.Stub {
+ "; regranting permissions for internal storage");
mSettings.mInternalSdkPlatform = mSdkVersion;
- updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions);
+ updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
- mSettings.writeLP();
+ // can downgrade to reader
+ mSettings.writeLPr();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
@@ -1057,10 +1070,12 @@ 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);
+ int retCode = mInstaller.remove(ps.name, 0);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
+ } else {
+ mUserManager.removePackageForAllUsers(ps.name);
}
} else {
//for emulator
@@ -1078,7 +1093,7 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Unable to remove old code file: " + ps.resourcePath);
}
}
- mSettings.removePackageLP(ps.name);
+ mSettings.removePackageLPw(ps.name);
}
void readPermissions() {
@@ -1345,16 +1360,16 @@ class PackageManagerService extends IPackageManager.Stub {
}
public PackageInfo getPackageInfo(String packageName, int flags) {
+ // reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
- if (Config.LOGV) Log.v(
- TAG, "getPackageInfo " + packageName
- + ": " + p);
+ if (DEBUG_PACKAGE_INFO)
+ Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
return generatePackageInfo(p, flags);
}
if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- return generatePackageInfoFromSettingsLP(packageName, flags);
+ return generatePackageInfoFromSettingsLPw(packageName, flags);
}
}
return null;
@@ -1362,6 +1377,7 @@ class PackageManagerService extends IPackageManager.Stub {
public String[] currentToCanonicalPackageNames(String[] names) {
String[] out = new String[names.length];
+ // reader
synchronized (mPackages) {
for (int i=names.length-1; i>=0; i--) {
PackageSetting ps = mSettings.mPackages.get(names[i]);
@@ -1373,6 +1389,7 @@ class PackageManagerService extends IPackageManager.Stub {
public String[] canonicalToCurrentPackageNames(String[] names) {
String[] out = new String[names.length];
+ // reader
synchronized (mPackages) {
for (int i=names.length-1; i>=0; i--) {
String cur = mSettings.mRenamedPackages.get(names[i]);
@@ -1383,6 +1400,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public int getPackageUid(String packageName) {
+ // reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if(p != null) {
@@ -1398,11 +1416,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
public int[] getPackageGids(String packageName) {
+ // reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
- if (Config.LOGV) Log.v(
- TAG, "getPackageGids" + packageName
- + ": " + p);
+ if (DEBUG_PACKAGE_INFO)
+ Log.v(TAG, "getPackageGids" + packageName + ": " + p);
if (p != null) {
final PackageSetting ps = (PackageSetting)p.mExtras;
final SharedUserSetting suid = ps.sharedUser;
@@ -1427,6 +1445,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public PermissionInfo getPermissionInfo(String name, int flags) {
+ // reader
synchronized (mPackages) {
final BasePermission p = mSettings.mPermissions.get(name);
if (p != null) {
@@ -1437,6 +1456,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {
+ // reader
synchronized (mPackages) {
ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
for (BasePermission p : mSettings.mPermissions.values()) {
@@ -1459,6 +1479,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {
+ // reader
synchronized (mPackages) {
return PackageParser.generatePermissionGroupInfo(
mPermissionGroups.get(name), flags);
@@ -1466,6 +1487,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+ // reader
synchronized (mPackages) {
final int N = mPermissionGroups.size();
ArrayList<PermissionGroupInfo> out
@@ -1477,12 +1499,12 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- private ApplicationInfo generateApplicationInfoFromSettingsLP(String packageName, int flags) {
+ private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags) {
PackageSetting ps = mSettings.mPackages.get(packageName);
- if(ps != null) {
- if(ps.pkg == null) {
- PackageInfo pInfo = generatePackageInfoFromSettingsLP(packageName, flags);
- if(pInfo != null) {
+ if (ps != null) {
+ if (ps.pkg == null) {
+ PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags);
+ if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
@@ -1492,16 +1514,17 @@ class PackageManagerService extends IPackageManager.Stub {
return null;
}
- private PackageInfo generatePackageInfoFromSettingsLP(String packageName, int flags) {
+ private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags) {
PackageSetting ps = mSettings.mPackages.get(packageName);
- if(ps != null) {
- if(ps.pkg == null) {
+ if (ps != null) {
+ if (ps.pkg == null) {
ps.pkg = new PackageParser.Package(packageName);
ps.pkg.applicationInfo.packageName = packageName;
ps.pkg.applicationInfo.flags = ps.pkgFlags;
ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
ps.pkg.applicationInfo.sourceDir = ps.codePathString;
- ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath();
+ ps.pkg.applicationInfo.dataDir =
+ getDataPathForPackage(ps.pkg.packageName, 0).getPath();
ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
ps.pkg.mSetEnabled = ps.enabled;
ps.pkg.mSetStopped = ps.stopped;
@@ -1512,9 +1535,10 @@ class PackageManagerService extends IPackageManager.Stub {
}
public ApplicationInfo getApplicationInfo(String packageName, int flags) {
+ // writer
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
- if (Config.LOGV) Log.v(
+ if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getApplicationInfo " + packageName
+ ": " + p);
if (p != null) {
@@ -1525,7 +1549,7 @@ class PackageManagerService extends IPackageManager.Stub {
return mAndroidApplication;
}
if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- return generateApplicationInfoFromSettingsLP(packageName, flags);
+ return generateApplicationInfoFromSettingsLPw(packageName, flags);
}
}
return null;
@@ -1589,8 +1613,8 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
- if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLP(a.info, flags)) {
+ if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
+ if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
return PackageParser.generateActivityInfo(a, flags);
}
if (mResolveComponentName.equals(component)) {
@@ -1603,9 +1627,9 @@ class PackageManagerService extends IPackageManager.Stub {
public ActivityInfo getReceiverInfo(ComponentName component, int flags) {
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
- if (Config.LOGV) Log.v(
+ if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLP(a.info, flags)) {
+ if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
return PackageParser.generateActivityInfo(a, flags);
}
}
@@ -1615,9 +1639,9 @@ class PackageManagerService extends IPackageManager.Stub {
public ServiceInfo getServiceInfo(ComponentName component, int flags) {
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
- if (Config.LOGV) Log.v(
+ if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
- if (s != null && mSettings.isEnabledLP(s.info, flags)) {
+ if (s != null && mSettings.isEnabledLPr(s.info, flags)) {
return PackageParser.generateServiceInfo(s, flags);
}
}
@@ -1627,9 +1651,9 @@ class PackageManagerService extends IPackageManager.Stub {
public ProviderInfo getProviderInfo(ComponentName component, int flags) {
synchronized (mPackages) {
PackageParser.Provider p = mProvidersByComponent.get(component);
- if (Config.LOGV) Log.v(
+ if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
- if (p != null && mSettings.isEnabledLP(p.info, flags)) {
+ if (p != null && mSettings.isEnabledLPr(p.info, flags)) {
return PackageParser.generateProviderInfo(p, flags);
}
}
@@ -1693,7 +1717,7 @@ class PackageManagerService extends IPackageManager.Stub {
public int checkUidPermission(String permName, int uid) {
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLP(uid);
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj != null) {
GrantedPermissions gp = (GrantedPermissions)obj;
if (gp.grantedPermissions.contains(permName)) {
@@ -1798,7 +1822,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (changed) {
if (!async) {
- mSettings.writeLP();
+ mSettings.writeLPr();
} else {
scheduleWriteSettingsLocked();
}
@@ -1829,7 +1853,7 @@ class PackageManagerService extends IPackageManager.Stub {
+ name);
}
mSettings.mPermissions.remove(name);
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
}
@@ -1842,21 +1866,22 @@ class PackageManagerService extends IPackageManager.Stub {
public int checkSignatures(String pkg1, String pkg2) {
synchronized (mPackages) {
- PackageParser.Package p1 = mPackages.get(pkg1);
- PackageParser.Package p2 = mPackages.get(pkg2);
+ final PackageParser.Package p1 = mPackages.get(pkg1);
+ final PackageParser.Package p2 = mPackages.get(pkg2);
if (p1 == null || p1.mExtras == null
|| p2 == null || p2.mExtras == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- return checkSignaturesLP(p1.mSignatures, p2.mSignatures);
+ return compareSignatures(p1.mSignatures, p2.mSignatures);
}
}
public int checkUidSignatures(int uid1, int uid2) {
+ // reader
synchronized (mPackages) {
Signature[] s1;
Signature[] s2;
- Object obj = mSettings.getUserIdLP(uid1);
+ Object obj = mSettings.getUserIdLPr(uid1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
s1 = ((SharedUserSetting)obj).signatures.mSignatures;
@@ -1868,7 +1893,7 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- obj = mSettings.getUserIdLP(uid2);
+ obj = mSettings.getUserIdLPr(uid2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
s2 = ((SharedUserSetting)obj).signatures.mSignatures;
@@ -1880,11 +1905,11 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- return checkSignaturesLP(s1, s2);
+ return compareSignatures(s1, s2);
}
}
- int checkSignaturesLP(Signature[] s1, Signature[] s2) {
+ static int compareSignatures(Signature[] s1, Signature[] s2) {
if (s1 == null) {
return s2 == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
@@ -1909,20 +1934,21 @@ class PackageManagerService extends IPackageManager.Stub {
}
public String[] getPackagesForUid(int uid) {
+ // reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLP(uid);
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
- SharedUserSetting sus = (SharedUserSetting)obj;
+ final SharedUserSetting sus = (SharedUserSetting) obj;
final int N = sus.packages.size();
- String[] res = new String[N];
- Iterator<PackageSetting> it = sus.packages.iterator();
- int i=0;
+ final String[] res = new String[N];
+ final Iterator<PackageSetting> it = sus.packages.iterator();
+ int i = 0;
while (it.hasNext()) {
res[i++] = it.next().name;
}
return res;
} else if (obj instanceof PackageSetting) {
- PackageSetting ps = (PackageSetting)obj;
+ final PackageSetting ps = (PackageSetting) obj;
return new String[] { ps.name };
}
}
@@ -1930,13 +1956,14 @@ class PackageManagerService extends IPackageManager.Stub {
}
public String getNameForUid(int uid) {
+ // reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLP(uid);
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
- SharedUserSetting sus = (SharedUserSetting)obj;
+ final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
} else if (obj instanceof PackageSetting) {
- PackageSetting ps = (PackageSetting)obj;
+ final PackageSetting ps = (PackageSetting) obj;
return ps.name;
}
}
@@ -1947,8 +1974,9 @@ class PackageManagerService extends IPackageManager.Stub {
if(sharedUserName == null) {
return -1;
}
+ // reader
synchronized (mPackages) {
- SharedUserSetting suid = mSettings.getSharedUserLP(sharedUserName, 0, false);
+ final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, false);
if(suid == null) {
return -1;
}
@@ -1973,11 +2001,9 @@ class PackageManagerService extends IPackageManager.Stub {
// then let the user decide between them.
ResolveInfo r0 = query.get(0);
ResolveInfo r1 = query.get(1);
- if (false) {
- System.out.println(r0.activityInfo.name +
- "=" + r0.priority + " vs " +
- r1.activityInfo.name +
- "=" + r1.priority);
+ if (DEBUG_INTENT_MATCHING) {
+ Log.d(TAG, r0.activityInfo.name + "=" + r0.priority + " vs "
+ + r1.activityInfo.name + "=" + r1.priority);
}
// If the first activity has a higher priority, or a different
// default, then it is always desireable to pick it.
@@ -2001,6 +2027,7 @@ class PackageManagerService extends IPackageManager.Stub {
ResolveInfo findPreferredActivity(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, int priority) {
+ // writer
synchronized (mPackages) {
if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
List<PreferredActivity> prefs =
@@ -2011,24 +2038,35 @@ class PackageManagerService extends IPackageManager.Stub {
// We will only allow preferred activities that came
// from the same match quality.
int match = 0;
+
+ if (DEBUG_PREFERRED) {
+ Log.v(TAG, "Figuring out best match...");
+ }
+
final int N = query.size();
- if (DEBUG_PREFERRED) Log.v(TAG, "Figuring out best match...");
for (int j=0; j<N; j++) {
- ResolveInfo ri = query.get(j);
- if (DEBUG_PREFERRED) Log.v(TAG, "Match for " + ri.activityInfo
- + ": 0x" + Integer.toHexString(match));
- if (ri.match > match) match = ri.match;
+ final ResolveInfo ri = query.get(j);
+ if (DEBUG_PREFERRED) {
+ Log.v(TAG, "Match for " + ri.activityInfo + ": 0x"
+ + Integer.toHexString(match));
+ }
+ if (ri.match > match) {
+ match = ri.match;
+ }
+ }
+
+ if (DEBUG_PREFERRED) {
+ Log.v(TAG, "Best match: 0x" + Integer.toHexString(match));
}
- if (DEBUG_PREFERRED) Log.v(TAG, "Best match: 0x"
- + Integer.toHexString(match));
+
match &= IntentFilter.MATCH_CATEGORY_MASK;
final int M = prefs.size();
for (int i=0; i<M; i++) {
- PreferredActivity pa = prefs.get(i);
+ final PreferredActivity pa = prefs.get(i);
if (pa.mPref.mMatch != match) {
continue;
}
- ActivityInfo ai = getActivityInfo(pa.mPref.mComponent, flags);
+ final ActivityInfo ai = getActivityInfo(pa.mPref.mComponent, flags);
if (DEBUG_PREFERRED) {
Log.v(TAG, "Got preferred activity:");
if (ai != null) {
@@ -2039,7 +2077,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (ai != null) {
for (int j=0; j<N; j++) {
- ResolveInfo ri = query.get(j);
+ final ResolveInfo ri = query.get(j);
if (!ri.activityInfo.applicationInfo.packageName
.equals(ai.applicationInfo.packageName)) {
continue;
@@ -2071,28 +2109,28 @@ class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags) {
- ComponentName comp = intent.getComponent();
+ final ComponentName comp = intent.getComponent();
if (comp != null) {
- List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
- ActivityInfo ai = getActivityInfo(comp, flags);
+ final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final ActivityInfo ai = getActivityInfo(comp, flags);
if (ai != null) {
- ResolveInfo ri = new ResolveInfo();
+ final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
+ // reader
synchronized (mPackages) {
- String pkgName = intent.getPackage();
+ final String pkgName = intent.getPackage();
if (pkgName == null) {
- return (List<ResolveInfo>)mActivities.queryIntent(intent,
- resolvedType, flags);
+ return mActivities.queryIntent(intent, resolvedType, flags);
}
- PackageParser.Package pkg = mPackages.get(pkgName);
+ final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent,
- resolvedType, flags, pkg.activities);
+ return mActivities.queryIntentForPackage(intent, resolvedType, flags,
+ pkg.activities);
}
return new ArrayList<ResolveInfo>();
}
@@ -2103,9 +2141,12 @@ class PackageManagerService extends IPackageManager.Stub {
String resolvedType, int flags) {
final String resultsAction = intent.getAction();
- List<ResolveInfo> results = queryIntentActivities(
- intent, resolvedType, flags|PackageManager.GET_RESOLVED_FILTER);
- if (Config.LOGV) Log.v(TAG, "Query " + intent + ": " + results);
+ List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags
+ | PackageManager.GET_RESOLVED_FILTER);
+
+ if (DEBUG_INTENT_MATCHING) {
+ Log.v(TAG, "Query " + intent + ": " + results);
+ }
int specificsPos = 0;
int N;
@@ -2125,16 +2166,21 @@ class PackageManagerService extends IPackageManager.Stub {
continue;
}
- if (Config.LOGV) Log.v(TAG, "Specific #" + i + ": " + sintent);
+ if (DEBUG_INTENT_MATCHING) {
+ Log.v(TAG, "Specific #" + i + ": " + sintent);
+ }
+
String action = sintent.getAction();
if (resultsAction != null && resultsAction.equals(action)) {
// If this action was explicitly requested, then don't
// remove things that have it.
action = null;
}
- ComponentName comp = sintent.getComponent();
+
ResolveInfo ri = null;
ActivityInfo ai = null;
+
+ ComponentName comp = sintent.getComponent();
if (comp == null) {
ri = resolveIntent(
sintent,
@@ -2158,7 +2204,7 @@ class PackageManagerService extends IPackageManager.Stub {
// Look for any generic query activities that are duplicates
// of this specific one, and remove them from the results.
- if (Config.LOGV) Log.v(TAG, "Specific #" + i + ": " + ai);
+ if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Specific #" + i + ": " + ai);
N = results.size();
int j;
for (j=specificsPos; j<N; j++) {
@@ -2168,7 +2214,7 @@ class PackageManagerService extends IPackageManager.Stub {
comp.getPackageName()))
|| (action != null && sri.filter.matchAction(action))) {
results.remove(j);
- if (Config.LOGV) Log.v(
+ if (DEBUG_INTENT_MATCHING) Log.v(
TAG, "Removing duplicate item from " + j
+ " due to specific " + specificsPos);
if (ri == null) {
@@ -2216,7 +2262,7 @@ class PackageManagerService extends IPackageManager.Stub {
final ResolveInfo rij = results.get(j);
if (rij.filter != null && rij.filter.hasAction(action)) {
results.remove(j);
- if (Config.LOGV) Log.v(
+ if (DEBUG_INTENT_MATCHING) Log.v(
TAG, "Removing duplicate item from " + j
+ " due to action " + action + " at " + i);
j--;
@@ -2255,12 +2301,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if (Config.LOGV) Log.v(TAG, "Result: " + results);
+ if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Result: " + results);
return results;
}
- public List<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, int flags) {
+ public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) {
ComponentName comp = intent.getComponent();
if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
@@ -2273,25 +2318,22 @@ class PackageManagerService extends IPackageManager.Stub {
return list;
}
+ // reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
- return (List<ResolveInfo>)mReceivers.queryIntent(intent,
- resolvedType, flags);
+ return mReceivers.queryIntent(intent, resolvedType, flags);
}
- PackageParser.Package pkg = mPackages.get(pkgName);
+ final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return (List<ResolveInfo>) mReceivers.queryIntentForPackage(intent,
- resolvedType, flags, pkg.receivers);
+ return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers);
}
return null;
}
}
- public ResolveInfo resolveService(Intent intent, String resolvedType,
- int flags) {
- List<ResolveInfo> query = queryIntentServices(intent, resolvedType,
- flags);
+ public ResolveInfo resolveService(Intent intent, String resolvedType, int flags) {
+ List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags);
if (query != null) {
if (query.size() >= 1) {
// If there is more than one service with the same priority,
@@ -2302,56 +2344,54 @@ class PackageManagerService extends IPackageManager.Stub {
return null;
}
- public List<ResolveInfo> queryIntentServices(Intent intent,
- String resolvedType, int flags) {
- ComponentName comp = intent.getComponent();
+ public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags) {
+ final ComponentName comp = intent.getComponent();
if (comp != null) {
- List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
- ServiceInfo si = getServiceInfo(comp, flags);
+ final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final ServiceInfo si = getServiceInfo(comp, flags);
if (si != null) {
- ResolveInfo ri = new ResolveInfo();
+ final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
list.add(ri);
}
return list;
}
+ // reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
- return (List<ResolveInfo>)mServices.queryIntent(intent,
- resolvedType, flags);
+ return mServices.queryIntent(intent, resolvedType, flags);
}
- PackageParser.Package pkg = mPackages.get(pkgName);
+ final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return (List<ResolveInfo>)mServices.queryIntentForPackage(intent,
- resolvedType, flags, pkg.services);
+ return mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services);
}
return null;
}
}
public List<PackageInfo> getInstalledPackages(int flags) {
- ArrayList<PackageInfo> finalList = new ArrayList<PackageInfo>();
+ final ArrayList<PackageInfo> finalList = new ArrayList<PackageInfo>();
+ // writer
synchronized (mPackages) {
if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- Iterator<PackageSetting> i = mSettings.mPackages.values().iterator();
+ final Iterator<PackageSetting> i = mSettings.mPackages.values().iterator();
while (i.hasNext()) {
final PackageSetting ps = i.next();
- PackageInfo psPkg = generatePackageInfoFromSettingsLP(ps.name, flags);
- if(psPkg != null) {
+ final PackageInfo psPkg = generatePackageInfoFromSettingsLPw(ps.name, flags);
+ if (psPkg != null) {
finalList.add(psPkg);
}
}
- }
- else {
- Iterator<PackageParser.Package> i = mPackages.values().iterator();
+ } else {
+ final Iterator<PackageParser.Package> i = mPackages.values().iterator();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null) {
- PackageInfo pi = generatePackageInfo(p, flags);
- if(pi != null) {
+ final PackageInfo pi = generatePackageInfo(p, flags);
+ if (pi != null) {
finalList.add(pi);
}
}
@@ -2362,20 +2402,20 @@ class PackageManagerService extends IPackageManager.Stub {
}
public List<ApplicationInfo> getInstalledApplications(int flags) {
- ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+ final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+ // writer
synchronized(mPackages) {
if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- Iterator<PackageSetting> i = mSettings.mPackages.values().iterator();
+ final Iterator<PackageSetting> i = mSettings.mPackages.values().iterator();
while (i.hasNext()) {
final PackageSetting ps = i.next();
- ApplicationInfo ai = generateApplicationInfoFromSettingsLP(ps.name, flags);
+ ApplicationInfo ai = generateApplicationInfoFromSettingsLPw(ps.name, flags);
if(ai != null) {
finalList.add(ai);
}
}
- }
- else {
- Iterator<PackageParser.Package> i = mPackages.values().iterator();
+ } else {
+ final Iterator<PackageParser.Package> i = mPackages.values().iterator();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null) {
@@ -2391,12 +2431,13 @@ class PackageManagerService extends IPackageManager.Stub {
}
public List<ApplicationInfo> getPersistentApplications(int flags) {
- ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+ final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+ // reader
synchronized (mPackages) {
- Iterator<PackageParser.Package> i = mPackages.values().iterator();
+ final Iterator<PackageParser.Package> i = mPackages.values().iterator();
while (i.hasNext()) {
- PackageParser.Package p = i.next();
+ final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
@@ -2409,10 +2450,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
public ProviderInfo resolveContentProvider(String name, int flags) {
+ // reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProviders.get(name);
return provider != null
- && mSettings.isEnabledLP(provider.info, flags)
+ && mSettings.isEnabledLPr(provider.info, flags)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
? PackageParser.generateProviderInfo(provider, flags)
@@ -2423,10 +2465,12 @@ class PackageManagerService extends IPackageManager.Stub {
/**
* @deprecated
*/
- public void querySyncProviders(List outNames, List outInfo) {
+ @Deprecated
+ public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) {
+ // reader
synchronized (mPackages) {
- Iterator<Map.Entry<String, PackageParser.Provider>> i
- = mProviders.entrySet().iterator();
+ final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
+ .iterator();
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
@@ -2446,22 +2490,22 @@ class PackageManagerService extends IPackageManager.Stub {
int uid, int flags) {
ArrayList<ProviderInfo> finalList = null;
+ // reader
synchronized (mPackages) {
- Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
+ final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
while (i.hasNext()) {
- PackageParser.Provider p = i.next();
+ final PackageParser.Provider p = i.next();
if (p.info.authority != null
- && (processName == null ||
- (p.info.processName.equals(processName)
- && p.info.applicationInfo.uid == uid))
- && mSettings.isEnabledLP(p.info, flags)
- && (!mSafeMode || (p.info.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) != 0)) {
+ && (processName == null
+ || (p.info.processName.equals(processName)
+ && p.info.applicationInfo.uid == uid))
+ && mSettings.isEnabledLPr(p.info, flags)
+ && (!mSafeMode
+ || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
- finalList.add(PackageParser.generateProviderInfo(p,
- flags));
+ finalList.add(PackageParser.generateProviderInfo(p, flags));
}
}
}
@@ -2475,6 +2519,7 @@ class PackageManagerService extends IPackageManager.Stub {
public InstrumentationInfo getInstrumentationInfo(ComponentName name,
int flags) {
+ // reader
synchronized (mPackages) {
final PackageParser.Instrumentation i = mInstrumentation.get(name);
return PackageParser.generateInstrumentationInfo(i, flags);
@@ -2486,10 +2531,11 @@ class PackageManagerService extends IPackageManager.Stub {
ArrayList<InstrumentationInfo> finalList =
new ArrayList<InstrumentationInfo>();
+ // reader
synchronized (mPackages) {
- Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator();
+ final Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator();
while (i.hasNext()) {
- PackageParser.Instrumentation p = i.next();
+ final PackageParser.Instrumentation p = i.next();
if (targetPackage == null
|| targetPackage.equals(p.info.targetPackage)) {
finalList.add(PackageParser.generateInstrumentationInfo(p,
@@ -2508,7 +2554,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
- if (false) {
+ if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir);
}
@@ -2538,7 +2584,7 @@ class PackageManagerService extends IPackageManager.Stub {
return fname;
}
- private static void reportSettingsProblem(int priority, String msg) {
+ static void reportSettingsProblem(int priority, String msg) {
try {
File fname = getSettingsProblemFile();
FileOutputStream out = new FileOutputStream(fname, true);
@@ -2602,17 +2648,18 @@ class PackageManagerService extends IPackageManager.Stub {
}
PackageSetting ps = null;
PackageSetting updatedPkg;
+ // reader
synchronized (mPackages) {
// Look to see if we already know about this package.
String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
// This package has been renamed to its original name. Let's
// use that.
- ps = mSettings.peekPackageLP(oldName);
+ ps = mSettings.peekPackageLPr(oldName);
}
// If there was no original package, see one for the real package name.
if (ps == null) {
- ps = mSettings.peekPackageLP(pkg.packageName);
+ ps = mSettings.peekPackageLPr(pkg.packageName);
}
// Check to see if this package could be hiding/updating a system
// package. Must look for it either under the original or real
@@ -2641,6 +2688,7 @@ class PackageManagerService extends IPackageManager.Stub {
// At this point, its safely assumed that package installation for
// apps in system partition will go through. If not there won't be a working
// version of the app
+ // writer
synchronized (mPackages) {
// Just remove the loaded entries from package lists.
mPackages.remove(ps.name);
@@ -2652,7 +2700,7 @@ class PackageManagerService extends IPackageManager.Stub {
InstallArgs args = new FileInstallArgs(ps.codePathString,
ps.resourcePathString, ps.nativeLibraryPathString);
args.cleanUpResourcesLI();
- mSettings.enableSystemPackageLP(ps.name);
+ mSettings.enableSystemPackageLPw(ps.name);
}
}
}
@@ -2710,7 +2758,7 @@ class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package pkg) {
if (pkgSetting.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
- if (checkSignaturesLP(pkgSetting.signatures.mSignatures, pkg.mSignatures) !=
+ if (compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures) !=
PackageManager.SIGNATURE_MATCH) {
Slog.e(TAG, "Package " + pkg.packageName
+ " signatures do not match the previously installed version; ignoring!");
@@ -2720,7 +2768,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Check for shared user signatures
if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
- if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures,
+ if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
Slog.e(TAG, "Package " + pkg.packageName
+ " has no signatures that match those in shared user "
@@ -2787,7 +2835,7 @@ class PackageManagerService extends IPackageManager.Stub {
return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
}
- private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) {
+ private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) {
if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
Slog.w(TAG, "Unable to update from " + oldPkg.name
+ " to " + newPkg.packageName
@@ -2802,11 +2850,15 @@ class PackageManagerService extends IPackageManager.Stub {
return true;
}
- private File getDataPathForPackage(PackageParser.Package pkg) {
- final File dataPath = new File(mAppDataDir, pkg.packageName);
- return dataPath;
+ File getDataPathForUser(int userId) {
+ return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId);
}
-
+
+ private File getDataPathForPackage(String packageName, int userId) {
+ return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId
+ + File.separator + packageName);
+ }
+
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime) {
File scanFile = new File(pkg.mScanPath);
@@ -2861,8 +2913,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) Log.d(
- TAG, "Scanning package " + pkg.packageName);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ Log.d(TAG, "Scanning package " + pkg.packageName);
+ }
+
if (mPackages.containsKey(pkg.packageName)
|| mSharedLibraries.containsKey(pkg.packageName)) {
Slog.w(TAG, "Application package " + pkg.packageName
@@ -2885,6 +2940,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.mAdoptPermissions = null;
}
+ // writer
synchronized (mPackages) {
// Check all shared libraries and map to their actual file path.
if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
@@ -2895,7 +2951,7 @@ class PackageManagerService extends IPackageManager.Stub {
int num = 0;
int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
for (int i=0; i<N; i++) {
- String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
+ final String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
if (file == null) {
Slog.e(TAG, "Package " + pkg.packageName
+ " requires unavailable shared library "
@@ -2908,7 +2964,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
for (int i=0; i<N; i++) {
- String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
+ final String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
if (file == null) {
Slog.w(TAG, "Package " + pkg.packageName
+ " desires unavailable shared library "
@@ -2926,7 +2982,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (pkg.mSharedUserId != null) {
- suid = mSettings.getSharedUserLP(pkg.mSharedUserId,
+ suid = mSettings.getSharedUserLPw(pkg.mSharedUserId,
pkg.applicationInfo.flags, true);
if (suid == null) {
Slog.w(TAG, "Creating application package " + pkg.packageName
@@ -2934,18 +2990,10 @@ class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) {
- Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid="
- + suid.userId + "): packages=" + suid.packages);
- }
- }
-
- if (false) {
- if (pkg.mOriginalPackages != null) {
- Log.w(TAG, "WAITING FOR DEBUGGER");
- Debug.waitForDebugger();
- Log.i(TAG, "Package " + pkg.packageName + " from original packages"
- + pkg.mOriginalPackages);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId
+ + "): packages=" + suid.packages);
}
}
@@ -2955,7 +3003,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (pkg.mOriginalPackages != null) {
// This package may need to be renamed to a previously
// installed name. Let's check on that...
- String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
+ final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
if (pkg.mOriginalPackages.contains(renamed)) {
// This package had originally been installed as the
// original name, and we have already taken care of
@@ -2971,11 +3019,11 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
- if ((origPackage=mSettings.peekPackageLP(
+ if ((origPackage = mSettings.peekPackageLPr(
pkg.mOriginalPackages.get(i))) != null) {
// We do have the package already installed under its
// original name... should we use it?
- if (!verifyPackageUpdate(origPackage, pkg)) {
+ if (!verifyPackageUpdateLPr(origPackage, pkg)) {
// New package is not compatible with original.
origPackage = null;
continue;
@@ -3006,7 +3054,7 @@ class PackageManagerService extends IPackageManager.Stub {
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
- pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile,
+ pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryDir,
pkg.applicationInfo.flags, true, false);
if (pkgSetting == null) {
@@ -3059,7 +3107,7 @@ class PackageManagerService extends IPackageManager.Stub {
// associated with an overall shared user, which doesn't seem all
// that unreasonable.
if (pkgSetting.sharedUser != null) {
- if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures,
+ if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser);
mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
@@ -3077,7 +3125,7 @@ class PackageManagerService extends IPackageManager.Stub {
// package isn't already installed, since we don't want to break
// things that are installed.
if ((scanMode&SCAN_NEW_INSTALL) != 0) {
- int N = pkg.providers.size();
+ final int N = pkg.providers.size();
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
@@ -3098,29 +3146,28 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
}
- }
- final String pkgName = pkg.packageName;
-
- if (pkg.mAdoptPermissions != null) {
- // This package wants to adopt ownership of permissions from
- // another package.
- for (int i=pkg.mAdoptPermissions.size()-1; i>=0; i--) {
- String origName = pkg.mAdoptPermissions.get(i);
- PackageSetting orig = mSettings.peekPackageLP(origName);
- if (orig != null) {
- if (verifyPackageUpdate(orig, pkg)) {
- Slog.i(TAG, "Adopting permissions from "
- + origName + " to " + pkg.packageName);
- mSettings.transferPermissions(origName, pkg.packageName);
+ if (pkg.mAdoptPermissions != null) {
+ // This package wants to adopt ownership of permissions from
+ // another package.
+ for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
+ final String origName = pkg.mAdoptPermissions.get(i);
+ final PackageSetting orig = mSettings.peekPackageLPr(origName);
+ if (orig != null) {
+ if (verifyPackageUpdateLPr(orig, pkg)) {
+ Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ + pkg.packageName);
+ mSettings.transferPermissionsLPw(origName, pkg.packageName);
+ }
}
}
}
}
+
+ final String pkgName = pkg.packageName;
final long scanFileTime = scanFile.lastModified();
final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0;
- final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.timeStamp;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName,
@@ -3133,7 +3180,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
// This is a normal package, need to make its data directory.
- dataPath = getDataPathForPackage(pkg);
+ dataPath = getDataPathForPackage(pkg.packageName, 0);
boolean uidError = false;
@@ -3149,8 +3196,11 @@ class PackageManagerService extends IPackageManager.Stub {
// If this is a system app, we can at least delete its
// current data so the application will still work.
if (mInstaller != null) {
- int ret = mInstaller.remove(pkgName);
+ int ret = mInstaller.remove(pkgName, 0);
if (ret >= 0) {
+ // TODO: Kill the processes first
+ // Remove the data directories for all users
+ mUserManager.removePackageForAllUsers(pkgName);
// Old data gone!
String msg = "System package " + pkg.packageName
+ " has changed from uid: "
@@ -3170,6 +3220,9 @@ class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName,
+ pkg.applicationInfo.uid);
}
}
if (!recovered) {
@@ -3185,6 +3238,7 @@ class PackageManagerService extends IPackageManager.Stub {
+ " has mismatched uid: "
+ mOutPermissions[1] + " on disk, "
+ pkg.applicationInfo.uid + " in settings";
+ // writer
synchronized (mPackages) {
mSettings.mReadMessages.append(msg);
mSettings.mReadMessages.append('\n');
@@ -3197,17 +3251,21 @@ class PackageManagerService extends IPackageManager.Stub {
}
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGV)
- Log.v(TAG, "Want this data dir: " + dataPath);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ 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,
pkg.applicationInfo.uid);
- if(ret < 0) {
+ if (ret < 0) {
// Error from installer
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
} else {
dataPath.mkdirs();
if (dataPath.exists()) {
@@ -3261,36 +3319,42 @@ class PackageManagerService extends IPackageManager.Stub {
* only for non-system apps and system app upgrades.
*/
if (pkg.applicationInfo.nativeLibraryDir != null) {
- final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
- final String dataPathString = dataPath.getPath();
+ try {
+ final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
+ final String dataPathString = dataPath.getCanonicalFile().getPath();
- if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
- /*
- * Upgrading from a previous version of the OS sometimes
- * leaves native libraries in the /data/data/<app>/lib
- * directory for system apps even when they shouldn't be.
- * Recent changes in the JNI library search path
- * necessitates we remove those to match previous behavior.
- */
- if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
- Log.i(TAG, "removed obsolete native libraries for system package " + path);
+ if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
+ /*
+ * Upgrading from a previous version of the OS sometimes
+ * leaves native libraries in the /data/data/<app>/lib
+ * directory for system apps even when they shouldn't be.
+ * Recent changes in the JNI library search path
+ * necessitates we remove those to match previous behavior.
+ */
+ if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
+ Log.i(TAG, "removed obsolete native libraries for system package "
+ + path);
+ }
+ } else if (nativeLibraryDir.getCanonicalFile().getParent()
+ .equals(dataPathString)) {
+ /*
+ * If this is an internal application or our
+ * nativeLibraryPath points to our data directory, unpack
+ * the libraries. The native library path pointing to the
+ * data directory for an application in an ASEC container
+ * can happen for older apps that existed before an OTA to
+ * Gingerbread.
+ */
+ Slog.i(TAG, "Unpacking native libraries for " + path);
+ mInstaller.unlinkNativeLibraryDirectory(dataPathString);
+ NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
+ } else {
+ Slog.i(TAG, "Linking native library dir for " + path);
+ mInstaller.linkNativeLibraryDirectory(dataPathString,
+ pkg.applicationInfo.nativeLibraryDir);
}
- } else if (nativeLibraryDir.getParent().equals(dataPathString)) {
- /*
- * If this is an internal application or our
- * nativeLibraryPath points to our data directory, unpack
- * the libraries. The native library path pointing to the
- * data directory for an application in an ASEC container
- * can happen for older apps that existed before an OTA to
- * Gingerbread.
- */
- Slog.i(TAG, "Unpacking native libraries for " + path);
- mInstaller.unlinkNativeLibraryDirectory(dataPathString);
- NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
- } else {
- Slog.i(TAG, "Linking native library dir for " + path);
- mInstaller.linkNativeLibraryDirectory(dataPathString,
- pkg.applicationInfo.nativeLibraryDir);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Unable to get canonical file " + ioe.toString());
}
}
pkg.mScanPath = path;
@@ -3316,13 +3380,14 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.uid);
}
+ // writer
synchronized (mPackages) {
// We don't expect installation to fail beyond this point,
if ((scanMode&SCAN_MONITOR) != 0) {
mAppDirs.put(pkg.mPath, pkg);
}
// Add the new setting to mSettings
- mSettings.insertPackageSettingLP(pkgSetting, pkg);
+ mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
@@ -3378,10 +3443,12 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
p.info.authority = p.info.authority + ";" + names[j];
}
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD)
- Log.d(TAG, "Registered content provider: " + names[j] +
- ", className = " + p.info.name +
- ", isSyncable = " + p.info.isSyncable);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ Log.d(TAG, "Registered content provider: " + names[j]
+ + ", className = " + p.info.name + ", isSyncable = "
+ + p.info.isSyncable);
+ }
} else {
PackageParser.Provider other = mProviders.get(names[j]);
Slog.w(TAG, "Skipping provider name " + names[j] +
@@ -3402,7 +3469,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Providers: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Providers: " + r);
}
N = pkg.services.size();
@@ -3422,7 +3489,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Services: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r);
}
N = pkg.receivers.size();
@@ -3442,7 +3509,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Receivers: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r);
}
N = pkg.activities.size();
@@ -3462,7 +3529,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Activities: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r);
}
N = pkg.permissionGroups.size();
@@ -3496,7 +3563,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Permission Groups: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r);
}
N = pkg.permissions.size();
@@ -3561,7 +3628,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Permissions: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permissions: " + r);
}
N = pkg.instrumentation.size();
@@ -3584,7 +3651,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r);
}
if (pkg.protectedBroadcasts != null) {
@@ -3613,25 +3680,15 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- // 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 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);
- }
- }
-
void removePackageLI(PackageParser.Package pkg, boolean chatty) {
- if (chatty && Config.LOGD) Log.d(
- TAG, "Removing package " + pkg.applicationInfo.packageName );
+ if (DEBUG_INSTALL) {
+ if (chatty)
+ Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName);
+ }
+ // writer
synchronized (mPackages) {
- clearPackagePreferredActivitiesLP(pkg.packageName);
+ clearPackagePreferredActivitiesLPw(pkg.packageName);
mPackages.remove(pkg.applicationInfo.packageName);
if (pkg.mPath != null) {
@@ -3657,10 +3714,12 @@ class PackageManagerService extends IPackageManager.Stub {
for (int j = 0; j < names.length; j++) {
if (mProviders.get(names[j]) == p) {
mProviders.remove(names[j]);
- if (chatty && Config.LOGD) Log.d(
- TAG, "Unregistered content provider: " + names[j] +
- ", className = " + p.info.name +
- ", isSyncable = " + p.info.isSyncable);
+ if (DEBUG_REMOVE) {
+ if (chatty)
+ Log.d(TAG, "Unregistered content provider: " + names[j]
+ + ", className = " + p.info.name + ", isSyncable = "
+ + p.info.isSyncable);
+ }
}
}
if (chatty) {
@@ -3673,7 +3732,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Providers: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Providers: " + r);
}
N = pkg.services.size();
@@ -3691,7 +3750,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Services: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Services: " + r);
}
N = pkg.receivers.size();
@@ -3709,7 +3768,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Receivers: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Receivers: " + r);
}
N = pkg.activities.size();
@@ -3727,17 +3786,15 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Activities: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r);
}
N = pkg.permissions.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Permission p = pkg.permissions.get(i);
- boolean tree = false;
BasePermission bp = mSettings.mPermissions.get(p.info.name);
if (bp == null) {
- tree = true;
bp = mSettings.mPermissionTrees.get(p.info.name);
}
if (bp != null && bp.perm == p) {
@@ -3753,7 +3810,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Permissions: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
}
N = pkg.instrumentation.size();
@@ -3771,7 +3828,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r);
}
}
}
@@ -3789,14 +3846,13 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
- private void updatePermissionsLP(String changingPkg,
+ private void updatePermissionsLPw(String changingPkg,
PackageParser.Package pkgInfo, boolean grantPermissions,
boolean replace, boolean replaceAll) {
// Make sure there are no dangling permission trees.
- Iterator<BasePermission> it = mSettings.mPermissionTrees
- .values().iterator();
+ Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
while (it.hasNext()) {
- BasePermission bp = it.next();
+ final BasePermission bp = it.next();
if (bp.packageSetting == null) {
// We may not yet have parsed the package, so just see if
// we still know about its settings.
@@ -3820,13 +3876,13 @@ class PackageManagerService extends IPackageManager.Stub {
// and make sure there are no dangling permissions.
it = mSettings.mPermissions.values().iterator();
while (it.hasNext()) {
- BasePermission bp = it.next();
+ final BasePermission bp = it.next();
if (bp.type == BasePermission.TYPE_DYNAMIC) {
if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
+ bp.name + " pkg=" + bp.sourcePackage
+ " info=" + bp.pendingInfo);
if (bp.packageSetting == null && bp.pendingInfo != null) {
- BasePermission tree = findPermissionTreeLP(bp.name);
+ final BasePermission tree = findPermissionTreeLP(bp.name);
if (tree != null) {
bp.packageSetting = tree.packageSetting;
bp.perm = new PackageParser.Permission(tree.perm.owner,
@@ -3861,18 +3917,18 @@ class PackageManagerService extends IPackageManager.Stub {
if (grantPermissions) {
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {
- grantPermissionsLP(pkg, replaceAll);
+ grantPermissionsLPw(pkg, replaceAll);
}
}
}
if (pkgInfo != null) {
- grantPermissionsLP(pkgInfo, replace);
+ grantPermissionsLPw(pkgInfo, replace);
}
}
- private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) {
- final PackageSetting ps = (PackageSetting)pkg.mExtras;
+ private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
return;
}
@@ -3893,12 +3949,11 @@ class PackageManagerService extends IPackageManager.Stub {
final int N = pkg.requestedPermissions.size();
for (int i=0; i<N; i++) {
- String name = pkg.requestedPermissions.get(i);
- BasePermission bp = mSettings.mPermissions.get(name);
- if (false) {
+ final String name = pkg.requestedPermissions.get(i);
+ final BasePermission bp = mSettings.mPermissions.get(name);
+ if (DEBUG_INSTALL) {
if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " checking " + name
- + ": " + bp);
+ Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
}
}
if (bp != null && bp.packageSetting != null) {
@@ -3913,10 +3968,10 @@ class PackageManagerService extends IPackageManager.Stub {
allowed = false;
} else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
|| bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- allowed = (checkSignaturesLP(
+ allowed = (compareSignatures(
bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
- || (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
+ || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
if (!allowed && bp.protectionLevel
== PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -3924,8 +3979,8 @@ class PackageManagerService extends IPackageManager.Stub {
// For updated system applications, the signatureOrSystem permission
// is granted only if it had been defined by the original application.
if (isUpdatedSystemApp(pkg)) {
- PackageSetting sysPs = mSettings.getDisabledSystemPkg(
- pkg.packageName);
+ final PackageSetting sysPs = mSettings
+ .getDisabledSystemPkgLPr(pkg.packageName);
final GrantedPermissions origGp = sysPs.sharedUser != null
? sysPs.sharedUser : sysPs;
if (origGp.grantedPermissions.contains(perm)) {
@@ -3944,7 +3999,7 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
allowed = false;
}
- if (false) {
+ if (DEBUG_INSTALL) {
if (gp != ps) {
Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
}
@@ -4022,25 +4077,26 @@ class PackageManagerService extends IPackageManager.Stub {
private final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
- public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
+ boolean defaultOnly) {
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
return super.queryIntent(intent, resolvedType, defaultOnly);
}
- public List queryIntent(Intent intent, String resolvedType, int flags) {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags) {
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
}
- public List queryIntentForPackage(Intent intent, String resolvedType, int flags,
- ArrayList<PackageParser.Activity> packageActivities) {
+ public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
+ int flags, ArrayList<PackageParser.Activity> packageActivities) {
if (packageActivities == null) {
return null;
}
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
- int N = packageActivities.size();
+ final int N = packageActivities.size();
ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut =
new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N);
@@ -4057,11 +4113,13 @@ class PackageManagerService extends IPackageManager.Stub {
public final void addActivity(PackageParser.Activity a, String type) {
final boolean systemApp = isSystemApp(a.info.applicationInfo);
mActivities.put(a.getComponentName(), a);
- if (SHOW_INFO || Config.LOGV) Log.v(
+ if (DEBUG_SHOW_INFO)
+ Log.v(
TAG, " " + type + " " +
(a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":");
- if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name);
- int NI = a.intents.size();
+ if (DEBUG_SHOW_INFO)
+ Log.v(TAG, " Class=" + a.info.name);
+ final int NI = a.intents.size();
for (int j=0; j<NI; j++) {
PackageParser.ActivityIntentInfo intent = a.intents.get(j);
if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {
@@ -4069,7 +4127,7 @@ class PackageManagerService extends IPackageManager.Stub {
Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "
+ a.className + " with priority > 0, forcing to 0");
}
- if (SHOW_INFO || Config.LOGV) {
+ if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
@@ -4082,14 +4140,16 @@ class PackageManagerService extends IPackageManager.Stub {
public final void removeActivity(PackageParser.Activity a, String type) {
mActivities.remove(a.getComponentName());
- if (SHOW_INFO || Config.LOGV) Log.v(
- TAG, " " + type + " " +
- (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":");
- if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name);
- int NI = a.intents.size();
+ if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " " + type + " "
+ + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel
+ : a.info.name) + ":");
+ Log.v(TAG, " Class=" + a.info.name);
+ }
+ final int NI = a.intents.size();
for (int j=0; j<NI; j++) {
PackageParser.ActivityIntentInfo intent = a.intents.get(j);
- if (SHOW_INFO || Config.LOGV) {
+ if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
@@ -4131,7 +4191,7 @@ class PackageManagerService extends IPackageManager.Stub {
@Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match) {
- if (!mSettings.isEnabledLP(info.activity.info, mFlags)) {
+ if (!mSettings.isEnabledLPr(info.activity.info, mFlags)) {
return null;
}
final PackageParser.Activity activity = info.activity;
@@ -4193,25 +4253,26 @@ class PackageManagerService extends IPackageManager.Stub {
private final class ServiceIntentResolver
extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> {
- public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
+ boolean defaultOnly) {
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
return super.queryIntent(intent, resolvedType, defaultOnly);
}
- public List queryIntent(Intent intent, String resolvedType, int flags) {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags) {
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
}
- public List queryIntentForPackage(Intent intent, String resolvedType, int flags,
- ArrayList<PackageParser.Service> packageServices) {
+ public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
+ int flags, ArrayList<PackageParser.Service> packageServices) {
if (packageServices == null) {
return null;
}
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
- int N = packageServices.size();
+ final int N = packageServices.size();
ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut =
new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N);
@@ -4227,16 +4288,17 @@ class PackageManagerService extends IPackageManager.Stub {
public final void addService(PackageParser.Service s) {
mServices.put(s.getComponentName(), s);
- if (SHOW_INFO || Config.LOGV) Log.v(
- TAG, " " + (s.info.nonLocalizedLabel != null
+ if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " "
+ + (s.info.nonLocalizedLabel != null
? s.info.nonLocalizedLabel : s.info.name) + ":");
- if (SHOW_INFO || Config.LOGV) Log.v(
- TAG, " Class=" + s.info.name);
- int NI = s.intents.size();
+ Log.v(TAG, " Class=" + s.info.name);
+ }
+ final int NI = s.intents.size();
int j;
for (j=0; j<NI; j++) {
PackageParser.ServiceIntentInfo intent = s.intents.get(j);
- if (SHOW_INFO || Config.LOGV) {
+ if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
@@ -4249,16 +4311,16 @@ class PackageManagerService extends IPackageManager.Stub {
public final void removeService(PackageParser.Service s) {
mServices.remove(s.getComponentName());
- if (SHOW_INFO || Config.LOGV) Log.v(
- TAG, " " + (s.info.nonLocalizedLabel != null
+ if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " " + (s.info.nonLocalizedLabel != null
? s.info.nonLocalizedLabel : s.info.name) + ":");
- if (SHOW_INFO || Config.LOGV) Log.v(
- TAG, " Class=" + s.info.name);
- int NI = s.intents.size();
+ Log.v(TAG, " Class=" + s.info.name);
+ }
+ final int NI = s.intents.size();
int j;
for (j=0; j<NI; j++) {
PackageParser.ServiceIntentInfo intent = s.intents.get(j);
- if (SHOW_INFO || Config.LOGV) {
+ if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
@@ -4301,7 +4363,7 @@ class PackageManagerService extends IPackageManager.Stub {
protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter,
int match) {
final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter;
- if (!mSettings.isEnabledLP(info.service.info, mFlags)) {
+ if (!mSettings.isEnabledLPr(info.service.info, mFlags)) {
return null;
}
final PackageParser.Service service = info.service;
@@ -4394,7 +4456,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
};
- private static final void sendPackageBroadcast(String action, String pkg,
+ static final void sendPackageBroadcast(String action, String pkg,
Bundle extras, String targetPkg, IIntentReceiver finishedReceiver) {
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
@@ -4425,6 +4487,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public String nextPackageToClean(String lastPackage) {
+ // writer
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
// If the external storage is no longer mounted at this point,
@@ -4445,6 +4508,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
void startCleaningPackages() {
+ // reader
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
return;
@@ -4477,6 +4541,7 @@ class PackageManagerService extends IPackageManager.Stub {
String addedPackage = null;
int addedUid = -1;
+ // TODO post a message to the handler to obtain serial ordering
synchronized (mInstallLock) {
String fullPathStr = null;
File fullPath = null;
@@ -4485,13 +4550,12 @@ class PackageManagerService extends IPackageManager.Stub {
fullPathStr = fullPath.getPath();
}
- if (Config.LOGV) Log.v(
- TAG, "File " + fullPathStr + " changed: "
- + Integer.toHexString(event));
+ if (DEBUG_APP_DIR_OBSERVER)
+ Log.v(TAG, "File " + fullPathStr + " changed: " + Integer.toHexString(event));
if (!isPackageFilename(path)) {
- if (Config.LOGV) Log.v(
- TAG, "Ignoring change of non-package file: " + fullPathStr);
+ if (DEBUG_APP_DIR_OBSERVER)
+ Log.v(TAG, "Ignoring change of non-package file: " + fullPathStr);
return;
}
@@ -4501,6 +4565,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
PackageParser.Package p = null;
+ // reader
synchronized (mPackages) {
p = mAppDirs.get(fullPathStr);
}
@@ -4522,8 +4587,14 @@ class PackageManagerService extends IPackageManager.Stub {
SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
System.currentTimeMillis());
if (p != null) {
+ /*
+ * TODO this seems dangerous as the package may have
+ * changed since we last acquired the mPackages
+ * lock.
+ */
+ // writer
synchronized (mPackages) {
- updatePermissionsLP(p.packageName, p,
+ updatePermissionsLPw(p.packageName, p,
p.permissions.size() > 0, false, false);
}
addedPackage = p.applicationInfo.packageName;
@@ -4532,8 +4603,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ // reader
synchronized (mPackages) {
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
@@ -4581,13 +4653,9 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
- public void setInstallerPackageName(String targetPackage,
- String installerPackageName) {
- PackageSetting pkgSetting;
+ public void setInstallerPackageName(String targetPackage, String installerPackageName) {
final int uid = Binder.getCallingUid();
- final int permission = mContext.checkCallingPermission(
- android.Manifest.permission.INSTALL_PACKAGES);
- final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ // writer
synchronized (mPackages) {
PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
if (targetPackageSetting == null) {
@@ -4606,7 +4674,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
Signature[] callerSignature;
- Object obj = mSettings.getUserIdLP(uid);
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature = ((SharedUserSetting)obj).signatures.mSignatures;
@@ -4622,7 +4690,7 @@ class PackageManagerService extends IPackageManager.Stub {
// Verify: can't set installerPackageName to a package that is
// not signed with the same cert as the caller.
if (installerPackageSetting != null) {
- if (checkSignaturesLP(callerSignature,
+ if (compareSignatures(callerSignature,
installerPackageSetting.signatures.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
@@ -4639,7 +4707,7 @@ class PackageManagerService extends IPackageManager.Stub {
// If the currently set package isn't valid, then it's always
// okay to change it.
if (setting != null) {
- if (checkSignaturesLP(callerSignature,
+ if (compareSignatures(callerSignature,
setting.signatures.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
@@ -4855,6 +4923,7 @@ class PackageManagerService extends IPackageManager.Stub {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
+ // reader
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null) {
@@ -4919,12 +4988,24 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Cannot install fwd locked apps on sdcard");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
+ final long lowThreshold;
+
+ final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager
+ .getService(DeviceStorageMonitorService.SERVICE);
+ if (dsm == null) {
+ Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
+ lowThreshold = 0L;
+ } else {
+ lowThreshold = dsm.getMemoryLowThreshold();
+ }
+
// Remote call to find out default install location
final PackageInfoLite pkgLite;
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags);
+ pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags,
+ lowThreshold);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -5144,10 +5225,26 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+ final long lowThreshold;
+
+ final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager
+ .getService(DeviceStorageMonitorService.SERVICE);
+ if (dsm == null) {
+ Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
+ lowThreshold = 0L;
+ } else {
+ if (dsm.isMemoryLow()) {
+ Log.w(TAG, "Memory is reported as being too low; aborting package install");
+ return false;
+ }
+
+ lowThreshold = dsm.getMemoryLowThreshold();
+ }
+
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return imcs.checkFreeStorage(false, packageURI);
+ return imcs.checkInternalFreeStorage(packageURI, lowThreshold);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -5373,7 +5470,7 @@ class PackageManagerService extends IPackageManager.Stub {
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return imcs.checkFreeStorage(true, packageURI);
+ return imcs.checkExternalFreeStorage(packageURI);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -5638,7 +5735,7 @@ class PackageManagerService extends IPackageManager.Stub {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
- boolean dataDirExists = getDataPathForPackage(pkg).exists();
+ boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
res.name = pkgName;
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
@@ -5697,7 +5794,7 @@ class PackageManagerService extends IPackageManager.Stub {
// First find the old package info and check signatures
synchronized(mPackages) {
oldPackage = mPackages.get(pkgName);
- if (checkSignaturesLP(oldPackage.mSignatures, pkg.mSignatures)
+ if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
return;
@@ -5720,11 +5817,6 @@ class PackageManagerService extends IPackageManager.Stub {
boolean deletedPkg = true;
boolean updatedSettings = false;
- String oldInstallerPackageName = null;
- synchronized (mPackages) {
- oldInstallerPackageName = mSettings.getInstallerPackageName(pkgName);
- }
-
long origUpdateTime;
if (pkg.mExtras != null) {
origUpdateTime = ((PackageSetting)pkg.mExtras).lastUpdateTime;
@@ -5788,10 +5880,12 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
// Restore of old package succeeded. Update permissions.
+ // writer
synchronized (mPackages) {
- updatePermissionsLP(deletedPackage.packageName, deletedPackage,
+ updatePermissionsLPw(deletedPackage.packageName, deletedPackage,
true, false, false);
- mSettings.writeLP();
+ // can downgrade to reader
+ mSettings.writeLPr();
}
Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
}
@@ -5814,6 +5908,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
PackageParser.Package oldPkg;
PackageSetting oldPkgSetting;
+ // reader
synchronized (mPackages) {
oldPkg = mPackages.get(packageName);
oldPkgSetting = mSettings.mPackages.get(packageName);
@@ -5830,8 +5925,9 @@ class PackageManagerService extends IPackageManager.Stub {
res.removedInfo.removedPackage = packageName;
// Remove existing system package
removePackageLI(oldPkg, true);
+ // writer
synchronized (mPackages) {
- if (!mSettings.disableSystemPackageLP(packageName) && deletedPackage != null) {
+ if (!mSettings.disableSystemPackageLPw(packageName) && deletedPackage != null) {
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
@@ -5875,11 +5971,11 @@ class PackageManagerService extends IPackageManager.Stub {
// Restore the old system information in Settings
synchronized(mPackages) {
if (updatedSettings) {
- mSettings.enableSystemPackageLP(packageName);
+ mSettings.enableSystemPackageLPw(packageName);
mSettings.setInstallerPackageName(packageName,
oldPkgSetting.installerPackageName);
}
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
}
@@ -5913,8 +6009,8 @@ class PackageManagerService extends IPackageManager.Stub {
//write settings. the installStatus will be incomplete at this stage.
//note that the new package setting would have already been
//added to mPackages. It hasn't been persisted yet.
- mSettings.setInstallStatus(pkgName, PKG_INSTALL_INCOMPLETE);
- mSettings.writeLP();
+ mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
+ mSettings.writeLPr();
}
if ((res.returnCode = moveDexFilesLI(newPackage))
@@ -5932,16 +6028,16 @@ class PackageManagerService extends IPackageManager.Stub {
Log.d(TAG, "New package installed in " + newPackage.mPath);
}
synchronized (mPackages) {
- updatePermissionsLP(newPackage.packageName, newPackage,
+ updatePermissionsLPw(newPackage.packageName, newPackage,
newPackage.permissions.size() > 0, true, false);
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
- mSettings.setInstallStatus(pkgName, PKG_INSTALL_COMPLETE);
+ mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
//to update install status
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
@@ -6040,7 +6136,6 @@ class PackageManagerService extends IPackageManager.Stub {
}
private int setPermissionsLI(PackageParser.Package newPackage) {
- String pkgName = newPackage.packageName;
int retCode = 0;
// TODO Gross hack but fix later. Ideally move this to be a post installation
// check after alloting uid.
@@ -6070,9 +6165,8 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (retCode != 0) {
- Slog.e(TAG, "Couldn't set new package file permissions for " +
- newPackage.mPath
- + ". The return code was: " + retCode);
+ Slog.e(TAG, "Couldn't set new package file permissions for " + newPackage.mPath
+ + ". The return code was: " + retCode);
// TODO Define new internal error
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
@@ -6321,37 +6415,46 @@ class PackageManagerService extends IPackageManager.Stub {
}
removePackageLI(p, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
- PackageSetting deletedPs;
+ final PackageSetting deletedPs;
+ // reader
synchronized (mPackages) {
deletedPs = mSettings.mPackages.get(packageName);
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (mInstaller != null) {
- int retCode = mInstaller.remove(packageName);
+ int retCode = mInstaller.remove(packageName, 0);
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 {
+ // TODO: Kill the processes first
+ mUserManager.removePackageForAllUsers(packageName);
}
} else {
// for simulator
- PackageParser.Package pkg = mPackages.get(packageName);
- File dataDir = new File(pkg.applicationInfo.dataDir);
+ File dataDir;
+ // reader
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(packageName);
+ dataDir = new File(pkg.applicationInfo.dataDir);
+ }
dataDir.delete();
}
schedulePackageCleaning(packageName);
}
+ // writer
synchronized (mPackages) {
if (deletedPs != null) {
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (outInfo != null) {
- outInfo.removedUid = mSettings.removePackageLP(packageName);
+ outInfo.removedUid = mSettings.removePackageLPw(packageName);
}
if (deletedPs != null) {
- updatePermissionsLP(deletedPs.name, null, false, false, false);
+ updatePermissionsLPw(deletedPs.name, null, false, false, false);
if (deletedPs.sharedUser != null) {
// remove permissions associated with package
- mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
+ mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
}
}
}
@@ -6366,9 +6469,10 @@ class PackageManagerService extends IPackageManager.Stub {
mSettings.mPreferredActivities.removeFilter(pa);
}
}
+ // can downgrade to reader
if (writeSettings) {
// Save settings now
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
}
@@ -6388,8 +6492,9 @@ class PackageManagerService extends IPackageManager.Stub {
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition
+ // reader
synchronized (mPackages) {
- ps = mSettings.getDisabledSystemPkg(p.packageName);
+ ps = mSettings.getDisabledSystemPkgLPr(p.packageName);
}
if (ps == null) {
Slog.w(TAG, "Attempt to delete unknown system package "+ p.packageName);
@@ -6411,9 +6516,10 @@ class PackageManagerService extends IPackageManager.Stub {
if (!ret) {
return false;
}
+ // writer
synchronized (mPackages) {
// Reinstate the old system package
- mSettings.enableSystemPackageLP(p.packageName);
+ mSettings.enableSystemPackageLPw(p.packageName);
// Remove any native libraries from the upgraded package.
NativeLibraryHelper.removeNativeBinariesLI(p.applicationInfo.nativeLibraryDir);
}
@@ -6426,10 +6532,12 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
return false;
}
+ // writer
synchronized (mPackages) {
- updatePermissionsLP(newPkg.packageName, newPkg, true, true, false);
+ updatePermissionsLPw(newPkg.packageName, newPkg, true, true, false);
+ // can downgrade to reader here
if (writeSettings) {
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
return true;
@@ -6581,7 +6689,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (mInstaller != null) {
- int retCode = mInstaller.clearUserData(packageName);
+ int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -6717,16 +6825,14 @@ class PackageManagerService extends IPackageManager.Stub {
return new ArrayList<PackageInfo>();
}
- int getUidTargetSdkVersionLockedLP(int uid) {
- Object obj = mSettings.getUserIdLP(uid);
+ private int getUidTargetSdkVersionLockedLPr(int uid) {
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
- SharedUserSetting sus = (SharedUserSetting)obj;
- final int N = sus.packages.size();
+ final SharedUserSetting sus = (SharedUserSetting) obj;
int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
- Iterator<PackageSetting> it = sus.packages.iterator();
- int i=0;
+ final Iterator<PackageSetting> it = sus.packages.iterator();
while (it.hasNext()) {
- PackageSetting ps = it.next();
+ final PackageSetting ps = it.next();
if (ps.pkg != null) {
int v = ps.pkg.applicationInfo.targetSdkVersion;
if (v < vers) vers = v;
@@ -6734,7 +6840,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
return vers;
} else if (obj instanceof PackageSetting) {
- PackageSetting ps = (PackageSetting)obj;
+ final PackageSetting ps = (PackageSetting) obj;
if (ps.pkg != null) {
return ps.pkg.applicationInfo.targetSdkVersion;
}
@@ -6744,11 +6850,12 @@ class PackageManagerService extends IPackageManager.Stub {
public void addPreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity) {
+ // writer
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
+ if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
+ Binder.getCallingUid());
@@ -6788,7 +6895,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
+ if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
+ Binder.getCallingUid());
@@ -6814,14 +6921,15 @@ class PackageManagerService extends IPackageManager.Stub {
}
public void clearPackagePreferredActivities(String packageName) {
+ final int uid = Binder.getCallingUid();
+ // writer
synchronized (mPackages) {
- int uid = Binder.getCallingUid();
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null || pkg.applicationInfo.uid != uid) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
+ if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid "
+ Binder.getCallingUid());
@@ -6832,13 +6940,13 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if (clearPackagePreferredActivitiesLP(packageName)) {
+ if (clearPackagePreferredActivitiesLPw(packageName)) {
scheduleWriteSettingsLocked();
}
}
}
- boolean clearPackagePreferredActivitiesLP(String packageName) {
+ boolean clearPackagePreferredActivitiesLPw(String packageName) {
boolean changed = false;
Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
while (it.hasNext()) {
@@ -6855,10 +6963,11 @@ class PackageManagerService extends IPackageManager.Stub {
List<ComponentName> outActivities, String packageName) {
int num = 0;
+ // reader
synchronized (mPackages) {
- Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
+ final Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
while (it.hasNext()) {
- PreferredActivity pa = it.next();
+ final PreferredActivity pa = it.next();
if (packageName == null
|| pa.mPref.mComponent.getPackageName().equals(packageName)) {
if (outFilters != null) {
@@ -6903,6 +7012,8 @@ class PackageManagerService extends IPackageManager.Stub {
String componentName = isApp ? packageName : className;
int packageUid = -1;
ArrayList<String> components;
+
+ // writer
synchronized (mPackages) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
@@ -6932,17 +7043,17 @@ class PackageManagerService extends IPackageManager.Stub {
// We're dealing with a component level state change
switch (newState) {
case COMPONENT_ENABLED_STATE_ENABLED:
- if (!pkgSetting.enableComponentLP(className)) {
+ if (!pkgSetting.enableComponentLPw(className)) {
return;
}
break;
case COMPONENT_ENABLED_STATE_DISABLED:
- if (!pkgSetting.disableComponentLP(className)) {
+ if (!pkgSetting.disableComponentLPw(className)) {
return;
}
break;
case COMPONENT_ENABLED_STATE_DEFAULT:
- if (!pkgSetting.restoreComponentLP(className)) {
+ if (!pkgSetting.restoreComponentLPw(className)) {
return;
}
break;
@@ -6951,10 +7062,10 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
}
- mSettings.writeLP();
+ mSettings.writeLPr();
packageUid = pkgSetting.userId;
components = mPendingBroadcasts.get(packageName);
- boolean newPackage = components == null;
+ final boolean newPackage = components == null;
if (newPackage) {
components = new ArrayList<String>();
}
@@ -6990,8 +7101,9 @@ class PackageManagerService extends IPackageManager.Stub {
private void sendPackageChangedBroadcast(String packageName,
boolean killFlag, ArrayList<String> componentNames, int packageUid) {
- if (false) Log.v(TAG, "Sending package changed: package=" + packageName
- + " components=" + componentNames);
+ if (DEBUG_INSTALL)
+ Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ + componentNames);
Bundle extras = new Bundle(4);
extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
String nameList[] = new String[componentNames.size()];
@@ -7003,72 +7115,37 @@ class PackageManagerService extends IPackageManager.Stub {
}
public void setPackageStoppedState(String packageName, boolean stopped) {
- PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ // writer
synchronized (mPackages) {
- pkgSetting = mSettings.mPackages.get(packageName);
- if (pkgSetting == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- if (!allowedByPermission && (uid != pkgSetting.userId)) {
- throw new SecurityException(
- "Permission Denial: attempt to change stopped state from pid="
- + Binder.getCallingPid()
- + ", uid=" + uid + ", package uid=" + pkgSetting.userId);
- }
- if (DEBUG_STOPPED && stopped) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Slog.i(TAG, "Stopping package " + packageName, e);
- }
- if (pkgSetting.stopped != stopped) {
- pkgSetting.stopped = stopped;
- pkgSetting.pkg.mSetStopped = stopped;
- if (pkgSetting.notLaunched) {
- if (pkgSetting.installerPackageName != null) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
- pkgSetting.name, null,
- pkgSetting.installerPackageName, null);
- }
- pkgSetting.notLaunched = false;
- }
+ if (mSettings.setPackageStoppedStateLPw(packageName, stopped, allowedByPermission,
+ uid)) {
scheduleWriteStoppedPackagesLocked();
}
}
}
public String getInstallerPackageName(String packageName) {
+ // reader
synchronized (mPackages) {
- PackageSetting pkg = mSettings.mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- return pkg.installerPackageName;
+ return mSettings.getInstallerPackageNameLPr(packageName);
}
}
- public int getApplicationEnabledSetting(String appPackageName) {
+ public int getApplicationEnabledSetting(String packageName) {
+ // reader
synchronized (mPackages) {
- PackageSetting pkg = mSettings.mPackages.get(appPackageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + appPackageName);
- }
- return pkg.enabled;
+ return mSettings.getApplicationEnabledSettingLPr(packageName);
}
}
public int getComponentEnabledSetting(ComponentName componentName) {
+ // reader
synchronized (mPackages) {
- final String packageNameStr = componentName.getPackageName();
- PackageSetting pkg = mSettings.mPackages.get(packageNameStr);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown component: " + componentName);
- }
- final String classNameStr = componentName.getClassName();
- return pkg.currentEnabledStateLP(classNameStr);
+ return mSettings.getComponentEnabledSettingLPr(componentName);
}
}
@@ -7112,6 +7189,76 @@ class PackageManagerService extends IPackageManager.Stub {
return buf.toString();
}
+ static class DumpState {
+ public static final int DUMP_LIBS = 1 << 0;
+
+ public static final int DUMP_FEATURES = 1 << 1;
+
+ public static final int DUMP_RESOLVERS = 1 << 2;
+
+ public static final int DUMP_PERMISSIONS = 1 << 3;
+
+ public static final int DUMP_PACKAGES = 1 << 4;
+
+ public static final int DUMP_SHARED_USERS = 1 << 5;
+
+ public static final int DUMP_MESSAGES = 1 << 6;
+
+ public static final int DUMP_PROVIDERS = 1 << 7;
+
+ public static final int OPTION_SHOW_FILTERS = 1 << 0;
+
+ private int mTypes;
+
+ private int mOptions;
+
+ private boolean mTitlePrinted;
+
+ private SharedUserSetting mSharedUser;
+
+ public boolean isDumping(int type) {
+ if (mTypes == 0) {
+ return true;
+ }
+
+ return (mTypes & type) != 0;
+ }
+
+ public void setDump(int type) {
+ mTypes |= type;
+ }
+
+ public boolean isOptionEnabled(int option) {
+ return (mOptions & option) != 0;
+ }
+
+ public void setOptionEnabled(int option) {
+ mOptions |= option;
+ }
+
+ public boolean onTitlePrinted() {
+ final boolean printed = mTitlePrinted;
+ mTitlePrinted = true;
+ return printed;
+ }
+
+ public boolean getTitlePrinted() {
+ return mTitlePrinted;
+ }
+
+ public void setTitlePrinted(boolean enabled) {
+ mTitlePrinted = enabled;
+ }
+
+ public SharedUserSetting getSharedUser() {
+ return mSharedUser;
+ }
+
+ public void setSharedUser(SharedUserSetting user) {
+ mSharedUser = user;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -7124,18 +7271,9 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
- boolean dumpStar = true;
- boolean dumpLibs = false;
- boolean dumpFeatures = false;
- boolean dumpResolvers = false;
- boolean dumpPermissions = false;
- boolean dumpPackages = false;
- boolean dumpSharedUsers = false;
- boolean dumpMessages = false;
- boolean dumpProviders = false;
+ DumpState dumpState = new DumpState();
String packageName = null;
- boolean showFilters = false;
int opti = 0;
while (opti < args.length) {
@@ -7163,7 +7301,7 @@ class PackageManagerService extends IPackageManager.Stub {
pw.println(" <package.name>: info about given package");
return;
} else if ("-f".equals(opt)) {
- showFilters = true;
+ dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
@@ -7177,40 +7315,31 @@ class PackageManagerService extends IPackageManager.Stub {
if ("android".equals(cmd) || cmd.contains(".")) {
packageName = cmd;
} else if ("l".equals(cmd) || "libraries".equals(cmd)) {
- dumpStar = false;
- dumpLibs = true;
+ dumpState.setDump(DumpState.DUMP_LIBS);
} else if ("f".equals(cmd) || "features".equals(cmd)) {
- dumpStar = false;
- dumpFeatures = true;
+ dumpState.setDump(DumpState.DUMP_FEATURES);
} else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
- dumpStar = false;
- dumpResolvers = true;
+ dumpState.setDump(DumpState.DUMP_RESOLVERS);
} else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
- dumpStar = false;
- dumpPermissions = true;
+ dumpState.setDump(DumpState.DUMP_PERMISSIONS);
} else if ("p".equals(cmd) || "packages".equals(cmd)) {
- dumpStar = false;
- dumpPackages = true;
+ dumpState.setDump(DumpState.DUMP_PACKAGES);
} else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
- dumpStar = false;
- dumpSharedUsers = true;
+ dumpState.setDump(DumpState.DUMP_SHARED_USERS);
} else if ("prov".equals(cmd) || "providers".equals(cmd)) {
- dumpStar = false;
- dumpProviders = true;
+ dumpState.setDump(DumpState.DUMP_PROVIDERS);
} else if ("m".equals(cmd) || "messages".equals(cmd)) {
- dumpStar = false;
- dumpMessages = true;
+ dumpState.setDump(DumpState.DUMP_MESSAGES);
}
}
-
- boolean printedTitle = false;
-
+
+ // reader
synchronized (mPackages) {
- if ((dumpStar || dumpLibs) && packageName == null) {
- if (printedTitle) pw.println(" ");
- printedTitle = true;
+ if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
pw.println("Libraries:");
- Iterator<String> it = mSharedLibraries.keySet().iterator();
+ final Iterator<String> it = mSharedLibraries.keySet().iterator();
while (it.hasNext()) {
String name = it.next();
pw.print(" ");
@@ -7220,9 +7349,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if ((dumpStar || dumpFeatures) && packageName == null) {
- if (printedTitle) pw.println(" ");
- printedTitle = true;
+ if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
pw.println("Features:");
Iterator<String> it = mAvailableFeatures.keySet().iterator();
while (it.hasNext()) {
@@ -7232,276 +7361,72 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if (dumpStar || dumpResolvers) {
- if (mActivities.dump(pw, printedTitle
- ? "\nActivity Resolver Table:" : "Activity Resolver Table:",
- " ", packageName, showFilters)) {
- printedTitle = true;
+ if (dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
+ if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
+ : "Activity Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
}
- if (mReceivers.dump(pw, printedTitle
- ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
- " ", packageName, showFilters)) {
- printedTitle = true;
+ if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
+ : "Receiver Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
}
- if (mServices.dump(pw, printedTitle
- ? "\nService Resolver Table:" : "Service Resolver Table:",
- " ", packageName, showFilters)) {
- printedTitle = true;
+ if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
+ : "Service Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
}
- if (mSettings.mPreferredActivities.dump(pw, printedTitle
- ? "\nPreferred Activities:" : "Preferred Activities:",
- " ", packageName, showFilters)) {
- printedTitle = true;
+ if (mSettings.mPreferredActivities.dump(pw,
+ dumpState.getTitlePrinted() ? "\nPreferred Activities:"
+ : "Preferred Activities:", " ",
+ packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
}
}
- boolean printedSomething = false;
- if (dumpStar || dumpPermissions) {
- for (BasePermission p : mSettings.mPermissions.values()) {
- if (packageName != null && !packageName.equals(p.sourcePackage)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Permissions:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" Permission ["); pw.print(p.name); pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(p)));
- pw.println("):");
- pw.print(" sourcePackage="); pw.println(p.sourcePackage);
- pw.print(" uid="); pw.print(p.uid);
- pw.print(" gids="); pw.print(arrayToString(p.gids));
- pw.print(" type="); pw.print(p.type);
- pw.print(" prot="); pw.println(p.protectionLevel);
- if (p.packageSetting != null) {
- pw.print(" packageSetting="); pw.println(p.packageSetting);
- }
- if (p.perm != null) {
- pw.print(" perm="); pw.println(p.perm);
- }
- }
+ if (dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
}
- if (dumpStar || dumpProviders) {
- printedSomething = false;
+ if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ boolean printedSomething = false;
for (PackageParser.Provider p : mProviders.values()) {
if (packageName != null && !packageName.equals(p.info.packageName)) {
continue;
}
if (!printedSomething) {
- if (printedTitle) pw.println(" ");
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
pw.println("Registered ContentProviders:");
printedSomething = true;
- printedTitle = true;
}
pw.print(" ["); pw.print(p.info.authority); pw.print("]: ");
pw.println(p.toString());
}
}
- printedSomething = false;
- SharedUserSetting packageSharedUser = null;
- if (dumpStar || dumpPackages) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Date date = new Date();
- for (PackageSetting ps : mSettings.mPackages.values()) {
- if (packageName != null && !packageName.equals(ps.realName)
- && !packageName.equals(ps.name)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Packages:");
- printedSomething = true;
- printedTitle = true;
- }
- packageSharedUser = ps.sharedUser;
- pw.print(" Package [");
- pw.print(ps.realName != null ? ps.realName : ps.name);
- pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(ps)));
- pw.println("):");
- if (ps.realName != null) {
- pw.print(" compat name="); pw.println(ps.name);
- }
- pw.print(" userId="); pw.print(ps.userId);
- pw.print(" gids="); pw.println(arrayToString(ps.gids));
- pw.print(" sharedUser="); pw.println(ps.sharedUser);
- 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(" versionCode="); pw.println(ps.versionCode);
- if (ps.pkg != null) {
- pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
- 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;
- pw.print("medium");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("large");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("xlarge");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("resizeable");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("anyDensity");
- }
- }
- pw.println("]");
- pw.print(" timeStamp=");
- date.setTime(ps.timeStamp); pw.println(sdf.format(date));
- pw.print(" firstInstallTime=");
- date.setTime(ps.firstInstallTime); pw.println(sdf.format(date));
- pw.print(" lastUpdateTime=");
- date.setTime(ps.lastUpdateTime); pw.println(sdf.format(date));
- if (ps.installerPackageName != null) {
- pw.print(" installerPackageName="); pw.println(ps.installerPackageName);
- }
- pw.print(" signatures="); pw.println(ps.signatures);
- pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
- pw.print(" haveGids="); pw.println(ps.haveGids);
- pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
- pw.print(" installStatus="); pw.print(ps.installStatus);
- pw.print(" stopped="); pw.print(ps.stopped);
- pw.print(" enabled="); pw.println(ps.enabled);
- if (ps.disabledComponents.size() > 0) {
- pw.println(" disabledComponents:");
- for (String s : ps.disabledComponents) {
- pw.print(" "); pw.println(s);
- }
- }
- if (ps.enabledComponents.size() > 0) {
- pw.println(" enabledComponents:");
- for (String s : ps.enabledComponents) {
- pw.print(" "); pw.println(s);
- }
- }
- if (ps.grantedPermissions.size() > 0) {
- pw.println(" grantedPermissions:");
- for (String s : ps.grantedPermissions) {
- pw.print(" "); pw.println(s);
- }
- }
- }
+ if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
+ mSettings.dumpPackagesLPr(pw, packageName, dumpState);
}
- printedSomething = false;
- if (dumpStar || dumpPackages) {
- if (mSettings.mRenamedPackages.size() > 0) {
- for (HashMap.Entry<String, String> e
- : mSettings.mRenamedPackages.entrySet()) {
- if (packageName != null && !packageName.equals(e.getKey())
- && !packageName.equals(e.getValue())) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Renamed packages:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" "); pw.print(e.getKey()); pw.print(" -> ");
- pw.println(e.getValue());
- }
- }
- printedSomething = false;
- if (mSettings.mDisabledSysPackages.size() > 0) {
- for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
- if (packageName != null && !packageName.equals(ps.realName)
- && !packageName.equals(ps.name)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Hidden system packages:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" Package [");
- pw.print(ps.realName != null ? ps.realName : ps.name);
- pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(ps)));
- pw.println("):");
- if (ps.realName != null) {
- pw.print(" compat name="); pw.println(ps.name);
- }
- pw.print(" userId="); pw.println(ps.userId);
- pw.print(" sharedUser="); pw.println(ps.sharedUser);
- pw.print(" codePath="); pw.println(ps.codePathString);
- pw.print(" resourcePath="); pw.println(ps.resourcePathString);
- }
- }
- }
- printedSomething = false;
- if (dumpStar || dumpSharedUsers) {
- for (SharedUserSetting su : mSettings.mSharedUsers.values()) {
- if (packageName != null && su != packageSharedUser) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Shared users:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" SharedUser ["); pw.print(su.name); pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(su)));
- pw.println("):");
- pw.print(" userId="); pw.print(su.userId);
- pw.print(" gids="); pw.println(arrayToString(su.gids));
- pw.println(" grantedPermissions:");
- for (String s : su.grantedPermissions) {
- pw.print(" "); pw.println(s);
- }
- }
+
+ if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ mSettings.dumpSharedUsersLPr(pw, packageName, dumpState);
}
-
- if ((dumpStar || dumpMessages) && packageName == null) {
- if (printedTitle) pw.println(" ");
- printedTitle = true;
- pw.println("Settings parse messages:");
- pw.print(mSettings.mReadMessages.toString());
-
+
+ if (dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ mSettings.dumpReadMessagesLPr(pw, dumpState);
+
pw.println(" ");
pw.println("Package warning messages:");
- File fname = getSettingsProblemFile();
+ final File fname = getSettingsProblemFile();
FileInputStream in = null;
try {
in = new FileInputStream(fname);
- int avail = in.available();
- byte[] data = new byte[avail];
+ final int avail = in.available();
+ final byte[] data = new byte[avail];
in.read(data);
pw.print(new String(data));
} catch (FileNotFoundException e) {
@@ -7510,2918 +7435,513 @@ class PackageManagerService extends IPackageManager.Stub {
if (in != null) {
try {
in.close();
- } catch (IOException e) {
- }
- }
- }
- }
- }
- }
-
- static final class BasePermission {
- final static int TYPE_NORMAL = 0;
- final static int TYPE_BUILTIN = 1;
- final static int TYPE_DYNAMIC = 2;
-
- final String name;
- String sourcePackage;
- PackageSettingBase packageSetting;
- final int type;
- int protectionLevel;
- PackageParser.Permission perm;
- PermissionInfo pendingInfo;
- int uid;
- int[] gids;
-
- BasePermission(String _name, String _sourcePackage, int _type) {
- name = _name;
- sourcePackage = _sourcePackage;
- type = _type;
- // Default to most conservative protection level.
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
- }
-
- public String toString() {
- return "BasePermission{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + name + "}";
- }
- }
-
- static class PackageSignatures {
- private Signature[] mSignatures;
-
- PackageSignatures(PackageSignatures orig) {
- if (orig != null && orig.mSignatures != null) {
- mSignatures = orig.mSignatures.clone();
- }
- }
-
- PackageSignatures(Signature[] sigs) {
- assignSignatures(sigs);
- }
-
- PackageSignatures() {
- }
-
- void writeXml(XmlSerializer serializer, String tagName,
- ArrayList<Signature> pastSignatures) throws IOException {
- if (mSignatures == null) {
- return;
- }
- serializer.startTag(null, tagName);
- serializer.attribute(null, "count",
- Integer.toString(mSignatures.length));
- for (int i=0; i<mSignatures.length; i++) {
- serializer.startTag(null, "cert");
- final Signature sig = mSignatures[i];
- final int sigHash = sig.hashCode();
- final int numPast = pastSignatures.size();
- int j;
- for (j=0; j<numPast; j++) {
- Signature pastSig = pastSignatures.get(j);
- if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) {
- serializer.attribute(null, "index", Integer.toString(j));
- break;
- }
- }
- if (j >= numPast) {
- pastSignatures.add(sig);
- serializer.attribute(null, "index", Integer.toString(numPast));
- serializer.attribute(null, "key", sig.toCharsString());
- }
- serializer.endTag(null, "cert");
- }
- serializer.endTag(null, tagName);
- }
-
- void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures)
- throws IOException, XmlPullParserException {
- String countStr = parser.getAttributeValue(null, "count");
- if (countStr == null) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <signatures> has"
- + " no count at " + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- }
- final int count = Integer.parseInt(countStr);
- mSignatures = new Signature[count];
- int pos = 0;
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("cert")) {
- if (pos < count) {
- String index = parser.getAttributeValue(null, "index");
- if (index != null) {
- try {
- int idx = Integer.parseInt(index);
- String key = parser.getAttributeValue(null, "key");
- if (key == null) {
- if (idx >= 0 && idx < pastSignatures.size()) {
- Signature sig = pastSignatures.get(idx);
- if (sig != null) {
- mSignatures[pos] = pastSignatures.get(idx);
- pos++;
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <cert> "
- + "index " + index + " is not defined at "
- + parser.getPositionDescription());
- }
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <cert> "
- + "index " + index + " is out of bounds at "
- + parser.getPositionDescription());
- }
- } else {
- while (pastSignatures.size() <= idx) {
- pastSignatures.add(null);
- }
- Signature sig = new Signature(key);
- pastSignatures.set(idx, sig);
- mSignatures[pos] = sig;
- pos++;
- }
- } catch (NumberFormatException e) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <cert> "
- + "index " + index + " is not a number at "
- + parser.getPositionDescription());
- }
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <cert> has"
- + " no index at " + parser.getPositionDescription());
+ } catch (IOException e) {
}
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: too "
- + "many <cert> tags, expected " + count
- + " at " + parser.getPositionDescription());
}
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <cert>: "
- + parser.getName());
- }
- XmlUtils.skipCurrentTag(parser);
- }
-
- if (pos < count) {
- // Should never happen -- there is an error in the written
- // settings -- but if it does we don't want to generate
- // a bad array.
- Signature[] newSigs = new Signature[pos];
- System.arraycopy(mSignatures, 0, newSigs, 0, pos);
- mSignatures = newSigs;
- }
- }
-
- private void assignSignatures(Signature[] sigs) {
- if (sigs == null) {
- mSignatures = null;
- return;
- }
- mSignatures = new Signature[sigs.length];
- for (int i=0; i<sigs.length; i++) {
- mSignatures[i] = sigs[i];
- }
- }
-
- @Override
- public String toString() {
- StringBuffer buf = new StringBuffer(128);
- buf.append("PackageSignatures{");
- buf.append(Integer.toHexString(System.identityHashCode(this)));
- buf.append(" [");
- if (mSignatures != null) {
- for (int i=0; i<mSignatures.length; i++) {
- if (i > 0) buf.append(", ");
- buf.append(Integer.toHexString(
- System.identityHashCode(mSignatures[i])));
}
}
- buf.append("]}");
- return buf.toString();
- }
- }
-
- static class PreferredActivity extends IntentFilter implements PreferredComponent.Callbacks {
- final PreferredComponent mPref;
-
- PreferredActivity(IntentFilter filter, int match, ComponentName[] set,
- ComponentName activity) {
- super(filter);
- mPref = new PreferredComponent(this, match, set, activity);
- }
-
- PreferredActivity(XmlPullParser parser) throws XmlPullParserException,
- IOException {
- mPref = new PreferredComponent(this, parser);
- }
-
- public void writeToXml(XmlSerializer serializer) throws IOException {
- mPref.writeToXml(serializer);
- serializer.startTag(null, "filter");
- super.writeToXml(serializer);
- serializer.endTag(null, "filter");
- }
-
- public boolean onReadTag(String tagName, XmlPullParser parser)
- throws XmlPullParserException, IOException {
- if (tagName.equals("filter")) {
- //Log.i(TAG, "Starting to parse filter...");
- readFromXml(parser);
- //Log.i(TAG, "Finished filter: outerDepth=" + outerDepth + " depth="
- // + parser.getDepth() + " tag=" + parser.getName());
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <preferred-activities>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- return true;
- }
- }
-
- static class GrantedPermissions {
- int pkgFlags;
-
- HashSet<String> grantedPermissions = new HashSet<String>();
- int[] gids;
-
- GrantedPermissions(int pkgFlags) {
- setFlags(pkgFlags);
- }
-
- GrantedPermissions(GrantedPermissions base) {
- pkgFlags = base.pkgFlags;
- grantedPermissions = (HashSet<String>) base.grantedPermissions.clone();
-
- if (base.gids != null) {
- gids = base.gids.clone();
- }
- }
-
- void setFlags(int pkgFlags) {
- this.pkgFlags = pkgFlags & (
- ApplicationInfo.FLAG_SYSTEM |
- ApplicationInfo.FLAG_FORWARD_LOCK |
- ApplicationInfo.FLAG_EXTERNAL_STORAGE);
- }
- }
-
- /**
- * Settings base class for pending and resolved classes.
- */
- static class PackageSettingBase extends GrantedPermissions {
- final String name;
- final String realName;
- File codePath;
- String codePathString;
- File resourcePath;
- String resourcePathString;
- String nativeLibraryPathString;
- long timeStamp;
- long firstInstallTime;
- long lastUpdateTime;
- int versionCode;
-
- boolean uidError;
-
- PackageSignatures signatures = new PackageSignatures();
-
- boolean permissionsFixed;
- boolean haveGids;
-
- // Whether this package is currently stopped, thus can not be
- // started until explicitly launched by the user.
- public boolean stopped;
-
- // Set to true if we have never launched this app.
- public boolean notLaunched;
-
- /* Explicitly disabled components */
- HashSet<String> disabledComponents = new HashSet<String>(0);
- /* Explicitly enabled components */
- HashSet<String> enabledComponents = new HashSet<String>(0);
- int enabled = COMPONENT_ENABLED_STATE_DEFAULT;
- int installStatus = PKG_INSTALL_COMPLETE;
-
- PackageSettingBase origPackage;
-
- /* package name of the app that installed this package */
- String installerPackageName;
-
- PackageSettingBase(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
- super(pkgFlags);
- this.name = name;
- this.realName = realName;
- init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
- }
-
- /**
- * New instance of PackageSetting with one-level-deep cloning.
- */
- PackageSettingBase(PackageSettingBase base) {
- super(base);
-
- name = base.name;
- realName = base.realName;
- codePath = base.codePath;
- codePathString = base.codePathString;
- resourcePath = base.resourcePath;
- resourcePathString = base.resourcePathString;
- nativeLibraryPathString = base.nativeLibraryPathString;
- timeStamp = base.timeStamp;
- firstInstallTime = base.firstInstallTime;
- lastUpdateTime = base.lastUpdateTime;
- versionCode = base.versionCode;
-
- uidError = base.uidError;
-
- signatures = new PackageSignatures(base.signatures);
-
- permissionsFixed = base.permissionsFixed;
- haveGids = base.haveGids;
- stopped = base.stopped;
- notLaunched = base.notLaunched;
-
- disabledComponents = (HashSet<String>) base.disabledComponents.clone();
-
- enabledComponents = (HashSet<String>) base.enabledComponents.clone();
-
- enabled = base.enabled;
- installStatus = base.installStatus;
-
- origPackage = base.origPackage;
-
- installerPackageName = base.installerPackageName;
- }
-
- void init(File codePath, File resourcePath, String nativeLibraryPathString,
- int pVersionCode) {
- this.codePath = codePath;
- this.codePathString = codePath.toString();
- this.resourcePath = resourcePath;
- this.resourcePathString = resourcePath.toString();
- this.nativeLibraryPathString = nativeLibraryPathString;
- this.versionCode = pVersionCode;
- }
-
- public void setInstallerPackageName(String packageName) {
- installerPackageName = packageName;
- }
-
- String getInstallerPackageName() {
- return installerPackageName;
- }
-
- public void setInstallStatus(int newStatus) {
- installStatus = newStatus;
- }
-
- public int getInstallStatus() {
- return installStatus;
- }
-
- public void setTimeStamp(long newStamp) {
- timeStamp = newStamp;
- }
-
- /**
- * Make a shallow copy of this package settings.
- */
- public void copyFrom(PackageSettingBase base) {
- grantedPermissions = base.grantedPermissions;
- gids = base.gids;
-
- timeStamp = base.timeStamp;
- firstInstallTime = base.firstInstallTime;
- lastUpdateTime = base.lastUpdateTime;
- signatures = base.signatures;
- permissionsFixed = base.permissionsFixed;
- haveGids = base.haveGids;
- stopped = base.stopped;
- notLaunched = base.notLaunched;
- disabledComponents = base.disabledComponents;
- enabledComponents = base.enabledComponents;
- enabled = base.enabled;
- installStatus = base.installStatus;
- }
-
- boolean enableComponentLP(String componentClassName) {
- boolean changed = disabledComponents.remove(componentClassName);
- changed |= enabledComponents.add(componentClassName);
- return changed;
- }
-
- boolean disableComponentLP(String componentClassName) {
- boolean changed = enabledComponents.remove(componentClassName);
- changed |= disabledComponents.add(componentClassName);
- return changed;
- }
-
- boolean restoreComponentLP(String componentClassName) {
- boolean changed = enabledComponents.remove(componentClassName);
- changed |= disabledComponents.remove(componentClassName);
- return changed;
- }
-
- int currentEnabledStateLP(String componentName) {
- if (enabledComponents.contains(componentName)) {
- return COMPONENT_ENABLED_STATE_ENABLED;
- } else if (disabledComponents.contains(componentName)) {
- return COMPONENT_ENABLED_STATE_DISABLED;
- } else {
- return COMPONENT_ENABLED_STATE_DEFAULT;
- }
- }
- }
-
- /**
- * Settings data for a particular package we know about.
- */
- static final class PackageSetting extends PackageSettingBase {
- int userId;
- PackageParser.Package pkg;
- SharedUserSetting sharedUser;
-
- PackageSetting(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
- pkgFlags);
- }
-
- /**
- * New instance of PackageSetting replicating the original settings.
- * Note that it keeps the same PackageParser.Package instance.
- */
- PackageSetting(PackageSetting orig) {
- super(orig);
-
- userId = orig.userId;
- pkg = orig.pkg;
- sharedUser = orig.sharedUser;
- }
-
- @Override
- public String toString() {
- return "PackageSetting{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + name + "/" + userId + "}";
}
}
- /**
- * Settings data for a particular shared user ID we know about.
- */
- static final class SharedUserSetting extends GrantedPermissions {
- final String name;
- int userId;
- final HashSet<PackageSetting> packages = new HashSet<PackageSetting>();
- final PackageSignatures signatures = new PackageSignatures();
-
- SharedUserSetting(String _name, int _pkgFlags) {
- super(_pkgFlags);
- name = _name;
- }
-
- @Override
- public String toString() {
- return "SharedUserSetting{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + name + "/" + userId + "}";
- }
- }
-
- /**
- * Holds information about dynamic settings.
- */
- private static final class Settings {
- private final File mSettingsFilename;
- private final File mBackupSettingsFilename;
- private final File mPackageListFilename;
- private final File mStoppedPackagesFilename;
- private final File mBackupStoppedPackagesFilename;
- private final HashMap<String, PackageSetting> mPackages =
- new HashMap<String, PackageSetting>();
- // List of replaced system applications
- final HashMap<String, PackageSetting> mDisabledSysPackages =
- new HashMap<String, PackageSetting>();
-
- // These are the last platform API version we were using for
- // the apps installed on internal and external storage. It is
- // used to grant newer permissions one time during a system upgrade.
- int mInternalSdkPlatform;
- int mExternalSdkPlatform;
-
- // The user's preferred activities associated with particular intent
- // filters.
- private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
- new IntentResolver<PreferredActivity, PreferredActivity>() {
- @Override
- protected String packageForFilter(PreferredActivity filter) {
- return filter.mPref.mComponent.getPackageName();
- }
- @Override
- protected void dumpFilter(PrintWriter out, String prefix,
- PreferredActivity filter) {
- filter.mPref.dump(out, prefix, filter);
- }
- };
- private final HashMap<String, SharedUserSetting> mSharedUsers =
- new HashMap<String, SharedUserSetting>();
- private final ArrayList<Object> mUserIds = new ArrayList<Object>();
- private final SparseArray<Object> mOtherUserIds =
- new SparseArray<Object>();
-
- // For reading/writing settings file.
- private final ArrayList<Signature> mPastSignatures =
- new ArrayList<Signature>();
-
- // Mapping from permission names to info about them.
- final HashMap<String, BasePermission> mPermissions =
- new HashMap<String, BasePermission>();
-
- // Mapping from permission tree names to info about them.
- final HashMap<String, BasePermission> mPermissionTrees =
- new HashMap<String, BasePermission>();
-
- // Packages that have been uninstalled and still need their external
- // storage data deleted.
- final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
-
- // Packages that have been renamed since they were first installed.
- // Keys are the new names of the packages, values are the original
- // names. The packages appear everwhere else under their original
- // names.
- final HashMap<String, String> mRenamedPackages = new HashMap<String, String>();
-
- private final StringBuilder mReadMessages = new StringBuilder();
-
- private static final class PendingPackage extends PackageSettingBase {
- final int sharedId;
-
- PendingPackage(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, nativeLibraryPathString,
- pVersionCode, pkgFlags);
- this.sharedId = sharedId;
- }
- }
- private final ArrayList<PendingPackage> mPendingPackages
- = new ArrayList<PendingPackage>();
-
- Settings() {
- File dataDir = Environment.getDataDirectory();
- File systemDir = new File(dataDir, "system");
- systemDir.mkdirs();
- FileUtils.setPermissions(systemDir.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");
- mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml");
- mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml");
- }
-
- PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage,
- String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
- final String name = pkg.packageName;
- PackageSetting p = getPackageLP(name, origPackage, realName, sharedUser, codePath,
- resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
- return p;
- }
-
- PackageSetting peekPackageLP(String name) {
- return mPackages.get(name);
- /*
- PackageSetting p = mPackages.get(name);
- if (p != null && p.codePath.getPath().equals(codePath)) {
- return p;
- }
- return null;
- */
- }
-
- void setInstallStatus(String pkgName, int status) {
- PackageSetting p = mPackages.get(pkgName);
- if(p != null) {
- if(p.getInstallStatus() != status) {
- p.setInstallStatus(status);
- }
- }
- }
+ // ------- apps on sdcard specific code -------
+ static final boolean DEBUG_SD_INSTALL = false;
- void setInstallerPackageName(String pkgName,
- String installerPkgName) {
- PackageSetting p = mPackages.get(pkgName);
- if(p != null) {
- p.setInstallerPackageName(installerPkgName);
- }
- }
+ private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
- String getInstallerPackageName(String pkgName) {
- PackageSetting p = mPackages.get(pkgName);
- return (p == null) ? null : p.getInstallerPackageName();
- }
+ private static final String SD_ENCRYPTION_ALGORITHM = "AES";
- int getInstallStatus(String pkgName) {
- PackageSetting p = mPackages.get(pkgName);
- if(p != null) {
- return p.getInstallStatus();
- }
- return -1;
- }
+ private boolean mMediaMounted = false;
- SharedUserSetting getSharedUserLP(String name,
- int pkgFlags, boolean create) {
- SharedUserSetting s = mSharedUsers.get(name);
- if (s == null) {
- if (!create) {
+ private String getEncryptKey() {
+ try {
+ String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
+ SD_ENCRYPTION_KEYSTORE_NAME);
+ if (sdEncKey == null) {
+ sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128,
+ SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME);
+ if (sdEncKey == null) {
+ Slog.e(TAG, "Failed to create encryption keys");
return null;
}
- s = new SharedUserSetting(name, pkgFlags);
- if (MULTIPLE_APPLICATION_UIDS) {
- s.userId = newUserIdLP(s);
- } else {
- s.userId = FIRST_APPLICATION_UID;
- }
- Log.i(TAG, "New shared user " + name + ": id=" + s.userId);
- // < 0 means we couldn't assign a userid; fall out and return
- // s, which is currently null
- if (s.userId >= 0) {
- mSharedUsers.put(name, s);
- }
- }
-
- return s;
- }
-
- boolean disableSystemPackageLP(String name) {
- PackageSetting p = mPackages.get(name);
- if(p == null) {
- Log.w(TAG, "Package:"+name+" is not an installed package");
- return false;
- }
- PackageSetting dp = mDisabledSysPackages.get(name);
- // always make sure the system package code and resource paths dont change
- if (dp == null) {
- if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
- p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- }
- mDisabledSysPackages.put(name, p);
-
- // a little trick... when we install the new package, we don't
- // want to modify the existing PackageSetting for the built-in
- // version. so at this point we need a new PackageSetting that
- // is okay to muck with.
- PackageSetting newp = new PackageSetting(p);
- replacePackageLP(name, newp);
- return true;
- }
- return false;
- }
-
- PackageSetting enableSystemPackageLP(String name) {
- PackageSetting p = mDisabledSysPackages.get(name);
- if(p == null) {
- Log.w(TAG, "Package:"+name+" is not disabled");
- return null;
- }
- // Reset flag in ApplicationInfo object
- if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
- p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- }
- PackageSetting ret = addPackageLP(name, p.realName, p.codePath, p.resourcePath,
- p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags);
- mDisabledSysPackages.remove(name);
- return ret;
- }
-
- PackageSetting addPackageLP(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
- PackageSetting p = mPackages.get(name);
- if (p != null) {
- if (p.userId == uid) {
- return p;
- }
- reportSettingsProblem(Log.ERROR,
- "Adding duplicate package, keeping first: " + name);
- return null;
- }
- p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
- vc, pkgFlags);
- p.userId = uid;
- if (addUserIdLP(uid, p, name)) {
- mPackages.put(name, p);
- return p;
}
+ return sdEncKey;
+ } catch (NoSuchAlgorithmException nsae) {
+ Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae);
return null;
- }
-
- SharedUserSetting addSharedUserLP(String name, int uid, int pkgFlags) {
- SharedUserSetting s = mSharedUsers.get(name);
- if (s != null) {
- if (s.userId == uid) {
- return s;
- }
- reportSettingsProblem(Log.ERROR,
- "Adding duplicate shared user, keeping first: " + name);
- return null;
- }
- s = new SharedUserSetting(name, pkgFlags);
- s.userId = uid;
- if (addUserIdLP(uid, s, name)) {
- mSharedUsers.put(name, s);
- return s;
- }
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Failed to retrieve encryption keys with exception: " + ioe);
return null;
}
- // Transfer ownership of permissions from one package to another.
- private void transferPermissions(String origPkg, String newPkg) {
- // Transfer ownership of permissions to the new package.
- for (int i=0; i<2; i++) {
- HashMap<String, BasePermission> permissions =
- i == 0 ? mPermissionTrees : mPermissions;
- for (BasePermission bp : permissions.values()) {
- if (origPkg.equals(bp.sourcePackage)) {
- if (DEBUG_UPGRADE) Log.v(TAG,
- "Moving permission " + bp.name
- + " from pkg " + bp.sourcePackage
- + " to " + newPkg);
- bp.sourcePackage = newPkg;
- bp.packageSetting = null;
- bp.perm = null;
- if (bp.pendingInfo != null) {
- bp.pendingInfo.packageName = newPkg;
- }
- bp.uid = 0;
- bp.gids = null;
- }
- }
- }
- }
-
- private PackageSetting getPackageLP(String name, PackageSetting origPackage,
- String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
- PackageSetting p = mPackages.get(name);
- if (p != null) {
- if (!p.codePath.equals(codePath)) {
- // Check to see if its a disabled system app
- if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- // This is an updated system app with versions in both system
- // and data partition. Just let the most recent version
- // take precedence.
- Slog.w(TAG, "Trying to update system app code path from " +
- p.codePathString + " to " + codePath.toString());
- } else {
- // Just a change in the code path is not an issue, but
- // let's log a message about it.
- Slog.i(TAG, "Package " + name + " codePath changed from " + p.codePath
- + " to " + codePath + "; Retaining data and using new");
- /*
- * Since we've changed paths, we need to prefer the new
- * native library path over the one stored in the
- * package settings since we might have moved from
- * internal to external storage or vice versa.
- */
- p.nativeLibraryPathString = nativeLibraryPathString;
- }
- }
- if (p.sharedUser != sharedUser) {
- reportSettingsProblem(Log.WARN,
- "Package " + name + " shared user changed from "
- + (p.sharedUser != null ? p.sharedUser.name : "<nothing>")
- + " to "
- + (sharedUser != null ? sharedUser.name : "<nothing>")
- + "; replacing with new");
- p = null;
- } else {
- if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
- // If what we are scanning is a system package, then
- // make it so, regardless of whether it was previously
- // installed only in the data partition.
- p.pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- }
- }
- }
- if (p == null) {
- // Create a new PackageSettings entry. this can end up here because
- // of code path mismatch or user id mismatch of an updated system partition
- if (!create) {
- return null;
- }
- if (origPackage != null) {
- // We are consuming the data from an existing package.
- p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
- nativeLibraryPathString, vc, pkgFlags);
- if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name
- + " is adopting original package " + origPackage.name);
- // Note that we will retain the new package's signature so
- // that we can keep its data.
- PackageSignatures s = p.signatures;
- p.copyFrom(origPackage);
- p.signatures = s;
- p.sharedUser = origPackage.sharedUser;
- p.userId = origPackage.userId;
- p.origPackage = origPackage;
- mRenamedPackages.put(name, origPackage.name);
- name = origPackage.name;
- // Update new package state.
- p.setTimeStamp(codePath.lastModified());
- } else {
- p = new PackageSetting(name, realName, codePath, resourcePath,
- nativeLibraryPathString, vc, pkgFlags);
- p.setTimeStamp(codePath.lastModified());
- p.sharedUser = sharedUser;
- // If this is not a system app, it starts out stopped.
- if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
- if (DEBUG_STOPPED) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Slog.i(TAG, "Stopping package " + name, e);
- }
- p.stopped = true;
- p.notLaunched = true;
- }
- if (sharedUser != null) {
- p.userId = sharedUser.userId;
- } else if (MULTIPLE_APPLICATION_UIDS) {
- // Clone the setting here for disabled system packages
- PackageSetting dis = mDisabledSysPackages.get(name);
- if (dis != null) {
- // For disabled packages a new setting is created
- // from the existing user id. This still has to be
- // added to list of user id's
- // Copy signatures from previous setting
- if (dis.signatures.mSignatures != null) {
- p.signatures.mSignatures = dis.signatures.mSignatures.clone();
- }
- p.userId = dis.userId;
- // Clone permissions
- p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
- // Clone component info
- p.disabledComponents = new HashSet<String>(dis.disabledComponents);
- p.enabledComponents = new HashSet<String>(dis.enabledComponents);
- // Add new setting to list of user ids
- addUserIdLP(p.userId, p, name);
- } else {
- // Assign new user id
- p.userId = newUserIdLP(p);
- }
- } else {
- p.userId = FIRST_APPLICATION_UID;
- }
- }
- if (p.userId < 0) {
- reportSettingsProblem(Log.WARN,
- "Package " + name + " could not be assigned a valid uid");
- return null;
- }
- if (add) {
- // Finish adding new package by adding it and updating shared
- // user preferences
- addPackageSettingLP(p, name, sharedUser);
- }
- }
- return p;
- }
-
- private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) {
- p.pkg = pkg;
- pkg.mSetEnabled = p.enabled;
- pkg.mSetStopped = p.stopped;
- final String codePath = pkg.applicationInfo.sourceDir;
- final String resourcePath = pkg.applicationInfo.publicSourceDir;
- // Update code path if needed
- if (!codePath.equalsIgnoreCase(p.codePathString)) {
- Slog.w(TAG, "Code path for pkg : " + p.pkg.packageName +
- " changing from " + p.codePathString + " to " + codePath);
- p.codePath = new File(codePath);
- p.codePathString = codePath;
- }
- //Update resource path if needed
- if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) {
- Slog.w(TAG, "Resource path for pkg : " + p.pkg.packageName +
- " changing from " + p.resourcePathString + " to " + resourcePath);
- 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;
- }
- // Update signatures if needed.
- if (p.signatures.mSignatures == null) {
- p.signatures.assignSignatures(pkg.mSignatures);
- }
- // If this app defines a shared user id initialize
- // the shared user signatures as well.
- if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
- p.sharedUser.signatures.assignSignatures(pkg.mSignatures);
- }
- addPackageSettingLP(p, pkg.packageName, p.sharedUser);
- }
-
- // Utility method that adds a PackageSetting to mPackages and
- // completes updating the shared user attributes
- private void addPackageSettingLP(PackageSetting p, String name,
- SharedUserSetting sharedUser) {
- mPackages.put(name, p);
- if (sharedUser != null) {
- if (p.sharedUser != null && p.sharedUser != sharedUser) {
- reportSettingsProblem(Log.ERROR,
- "Package " + p.name + " was user "
- + p.sharedUser + " but is now " + sharedUser
- + "; I am not changing its files so it will probably fail!");
- p.sharedUser.packages.remove(p);
- } else if (p.userId != sharedUser.userId) {
- reportSettingsProblem(Log.ERROR,
- "Package " + p.name + " was user id " + p.userId
- + " but is now user " + sharedUser
- + " with id " + sharedUser.userId
- + "; I am not changing its files so it will probably fail!");
- }
-
- sharedUser.packages.add(p);
- p.sharedUser = sharedUser;
- p.userId = sharedUser.userId;
- }
- }
+ }
- /*
- * Update the shared user setting when a package using
- * specifying the shared user id is removed. The gids
- * associated with each permission of the deleted package
- * are removed from the shared user's gid list only if its
- * not in use by other permissions of packages in the
- * shared user setting.
- */
- private void updateSharedUserPermsLP(PackageSetting deletedPs, int[] globalGids) {
- if ( (deletedPs == null) || (deletedPs.pkg == null)) {
- Slog.i(TAG, "Trying to update info for null package. Just ignoring");
- return;
- }
- // No sharedUserId
- if (deletedPs.sharedUser == null) {
- return;
- }
- SharedUserSetting sus = deletedPs.sharedUser;
- // Update permissions
- for (String eachPerm: deletedPs.pkg.requestedPermissions) {
- boolean used = false;
- if (!sus.grantedPermissions.contains (eachPerm)) {
+ /* 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;
}
- for (PackageSetting pkg:sus.packages) {
- if (pkg.pkg != null &&
- !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
- pkg.pkg.requestedPermissions.contains(eachPerm)) {
- used = true;
- break;
- }
- }
- if (!used) {
- // can safely delete this permission from list
- sus.grantedPermissions.remove(eachPerm);
- }
- }
- // Update gids
- int newGids[] = globalGids;
- for (String eachPerm : sus.grantedPermissions) {
- BasePermission bp = mPermissions.get(eachPerm);
- if (bp != null) {
- newGids = appendInts(newGids, bp.gids);
- }
- }
- sus.gids = newGids;
- }
- private int removePackageLP(String name) {
- PackageSetting p = mPackages.get(name);
- if (p != null) {
- mPackages.remove(name);
- if (p.sharedUser != null) {
- p.sharedUser.packages.remove(p);
- if (p.sharedUser.packages.size() == 0) {
- mSharedUsers.remove(p.sharedUser.name);
- removeUserIdLP(p.sharedUser.userId);
- return p.sharedUser.userId;
+ String subStr = name.substring(mTempContainerPrefix.length());
+ try {
+ int cid = Integer.parseInt(subStr);
+ if (cid >= tmpIdx) {
+ tmpIdx = cid + 1;
}
- } else {
- removeUserIdLP(p.userId);
- return p.userId;
- }
- }
- return -1;
- }
-
- private void replacePackageLP(String name, PackageSetting newp) {
- PackageSetting p = mPackages.get(name);
- if (p != null) {
- if (p.sharedUser != null) {
- p.sharedUser.packages.remove(p);
- p.sharedUser.packages.add(newp);
- } else {
- replaceUserIdLP(p.userId, newp);
- }
- }
- mPackages.put(name, newp);
- }
-
- private boolean addUserIdLP(int uid, Object obj, Object name) {
- if (uid >= FIRST_APPLICATION_UID + MAX_APPLICATION_UIDS) {
- return false;
- }
-
- if (uid >= FIRST_APPLICATION_UID) {
- int N = mUserIds.size();
- final int index = uid - FIRST_APPLICATION_UID;
- while (index >= N) {
- mUserIds.add(null);
- N++;
- }
- if (mUserIds.get(index) != null) {
- reportSettingsProblem(Log.ERROR,
- "Adding duplicate user id: " + uid
- + " name=" + name);
- return false;
- }
- mUserIds.set(index, obj);
- } else {
- if (mOtherUserIds.get(uid) != null) {
- reportSettingsProblem(Log.ERROR,
- "Adding duplicate shared id: " + uid
- + " name=" + name);
- return false;
- }
- mOtherUserIds.put(uid, obj);
- }
- return true;
- }
-
- public Object getUserIdLP(int uid) {
- if (uid >= FIRST_APPLICATION_UID) {
- int N = mUserIds.size();
- final int index = uid - FIRST_APPLICATION_UID;
- return index < N ? mUserIds.get(index) : null;
- } else {
- return mOtherUserIds.get(uid);
- }
- }
-
- private Set<String> findPackagesWithFlag(int flag) {
- Set<String> ret = new HashSet<String>();
- for (PackageSetting ps : mPackages.values()) {
- // Has to match atleast all the flag bits set on flag
- if ((ps.pkgFlags & flag) == flag) {
- ret.add(ps.name);
+ } catch (NumberFormatException e) {
}
}
- return ret;
}
+ return mTempContainerPrefix + tmpIdx;
+ }
- private void removeUserIdLP(int uid) {
- if (uid >= FIRST_APPLICATION_UID) {
- int N = mUserIds.size();
- final int index = uid - FIRST_APPLICATION_UID;
- if (index < N) mUserIds.set(index, null);
- } else {
- mOtherUserIds.remove(uid);
- }
+ /*
+ * Update media status on PackageManager.
+ */
+ public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("Media status can only be updated by the system");
}
-
- private void replaceUserIdLP(int uid, Object obj) {
- if (uid >= FIRST_APPLICATION_UID) {
- int N = mUserIds.size();
- final int index = uid - FIRST_APPLICATION_UID;
- if (index < N) mUserIds.set(index, obj);
- } else {
- mOtherUserIds.put(uid, obj);
- }
- }
-
- void writeStoppedLP() {
- // Keep the old stopped packages around until we know the new ones have
- // been successfully written.
- if (mStoppedPackagesFilename.exists()) {
- // Presence of backup settings file indicates that we failed
- // to persist packages earlier. So preserve the older
- // backup for future reference since the current packages
- // might have been corrupted.
- if (!mBackupStoppedPackagesFilename.exists()) {
- if (!mStoppedPackagesFilename.renameTo(mBackupStoppedPackagesFilename)) {
- Log.wtf(TAG, "Unable to backup package manager stopped packages, "
- + "current changes will be lost at reboot");
- return;
- }
- } else {
- mStoppedPackagesFilename.delete();
- Slog.w(TAG, "Preserving older stopped packages backup");
- }
- }
-
- try {
- FileOutputStream fstr = new FileOutputStream(mStoppedPackagesFilename);
- BufferedOutputStream str = new BufferedOutputStream(fstr);
-
- //XmlSerializer serializer = XmlUtils.serializerInstance();
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(str, "utf-8");
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
- serializer.startTag(null, "stopped-packages");
-
- for (PackageSetting pkg : mPackages.values()) {
- if (pkg.stopped) {
- serializer.startTag(null, "pkg");
- serializer.attribute(null, "name", pkg.name);
- if (pkg.notLaunched) {
- serializer.attribute(null, "nl", "1");
- }
- serializer.endTag(null, "pkg");
- }
- }
-
- serializer.endTag(null, "stopped-packages");
-
- serializer.endDocument();
-
- str.flush();
- FileUtils.sync(fstr);
- str.close();
-
- // New settings successfully written, old ones are no longer
- // needed.
- mBackupStoppedPackagesFilename.delete();
- FileUtils.setPermissions(mStoppedPackagesFilename.toString(),
- FileUtils.S_IRUSR|FileUtils.S_IWUSR
- |FileUtils.S_IRGRP|FileUtils.S_IWGRP
- |FileUtils.S_IROTH,
- -1, -1);
-
- // Done, all is good!
+ // reader; this apparently protects mMediaMounted, but should probably
+ // be a different lock in that case.
+ synchronized (mPackages) {
+ Log.i(TAG, "Updating external media status from "
+ + (mMediaMounted ? "mounted" : "unmounted") + " to "
+ + (mediaStatus ? "mounted" : "unmounted"));
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus
+ + ", mMediaMounted=" + mMediaMounted);
+ if (mediaStatus == mMediaMounted) {
+ final Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1
+ : 0, -1);
+ mHandler.sendMessage(msg);
return;
-
- } catch(java.io.IOException e) {
- Log.wtf(TAG, "Unable to write package manager stopped packages, "
- + " current changes will be lost at reboot", e);
- }
-
- // Clean up partially written files
- if (mStoppedPackagesFilename.exists()) {
- if (!mStoppedPackagesFilename.delete()) {
- Log.i(TAG, "Failed to clean up mangled file: " + mStoppedPackagesFilename);
- }
}
+ mMediaMounted = mediaStatus;
}
-
- // Note: assumed "stopped" field is already cleared in all packages.
- void readStoppedLP() {
- FileInputStream str = null;
- if (mBackupStoppedPackagesFilename.exists()) {
- try {
- str = new FileInputStream(mBackupStoppedPackagesFilename);
- mReadMessages.append("Reading from backup stopped packages file\n");
- reportSettingsProblem(Log.INFO, "Need to read from backup stopped packages file");
- if (mSettingsFilename.exists()) {
- // If both the backup and normal file exist, we
- // ignore the normal one since it might have been
- // corrupted.
- Slog.w(TAG, "Cleaning up stopped packages file "
- + mStoppedPackagesFilename);
- mStoppedPackagesFilename.delete();
- }
- } catch (java.io.IOException e) {
- // We'll try for the normal settings file.
- }
+ // Queue up an async operation since the package installation may take a
+ // little while.
+ mHandler.post(new Runnable() {
+ public void run() {
+ // TODO fix this; this does nothing.
+ mHandler.removeCallbacks(this);
+ updateExternalMediaStatusInner(mediaStatus, reportStatus);
}
+ });
+ }
- try {
- if (str == null) {
- if (!mStoppedPackagesFilename.exists()) {
- mReadMessages.append("No stopped packages file found\n");
- reportSettingsProblem(Log.INFO, "No stopped packages file file; "
- + "assuming all started");
- // At first boot, make sure no packages are stopped.
- // We usually want to have third party apps initialize
- // in the stopped state, but not at first boot.
- for (PackageSetting pkg : mPackages.values()) {
- pkg.stopped = false;
- pkg.notLaunched = false;
- }
- return;
- }
- str = new FileInputStream(mStoppedPackagesFilename);
- }
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, null);
-
- int type;
- while ((type=parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
-
- if (type != XmlPullParser.START_TAG) {
- mReadMessages.append("No start tag found in stopped packages file\n");
- reportSettingsProblem(Log.WARN,
- "No start tag found in package manager stopped packages");
- return;
- }
-
- int outerDepth = parser.getDepth();
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
+ /*
+ * Collect information of applications on external media, map them against
+ * existing containers and update information based on current mount status.
+ * Please note that we always have to report status if reportStatus has been
+ * set to true especially when unloading packages.
+ */
+ private void updateExternalMediaStatusInner(boolean mediaStatus, boolean reportStatus) {
+ // Collection of uids
+ int uidArr[] = null;
+ // Collection of stale containers
+ HashSet<String> removeCids = new HashSet<String>();
+ // Collection of packages on external media with valid containers.
+ HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
+ // Get list of secure containers.
+ final String list[] = PackageHelper.getSecureContainerList();
+ if (list == null || list.length == 0) {
+ Log.i(TAG, "No secure containers on sdcard");
+ } else {
+ // Process list of secure containers and categorize them
+ // as active or stale based on their package internal state.
+ int uidList[] = new int[list.length];
+ int num = 0;
+ // reader
+ synchronized (mPackages) {
+ for (String cid : list) {
+ SdInstallArgs args = new SdInstallArgs(cid);
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Processing container " + cid);
+ String pkgName = args.getPackageName();
+ if (pkgName == null) {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Container : " + cid + " stale");
+ removeCids.add(cid);
continue;
}
-
- String tagName = parser.getName();
- if (tagName.equals("pkg")) {
- String name = parser.getAttributeValue(null, "name");
- PackageSetting ps = mPackages.get(name);
- if (ps != null) {
- ps.stopped = true;
- if ("1".equals(parser.getAttributeValue(null, "nl"))) {
- ps.notLaunched = true;
- }
- } else {
- Slog.w(TAG, "No package known for stopped package: " + name);
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Looking for pkg : " + pkgName);
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ // The package status is changed only if the code path
+ // matches between settings and the container id.
+ if (ps != null && ps.codePathString != null
+ && ps.codePathString.equals(args.getCodePath())) {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName
+ + " at code path: " + ps.codePathString);
+ // We do have a valid package installed on sdcard
+ processCids.put(args, ps.codePathString);
+ int uid = ps.userId;
+ if (uid != -1) {
+ uidList[num++] = uid;
}
- XmlUtils.skipCurrentTag(parser);
} else {
- Slog.w(TAG, "Unknown element under <stopped-packages>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- str.close();
-
- } catch(XmlPullParserException e) {
- mReadMessages.append("Error reading: " + e.toString());
- reportSettingsProblem(Log.ERROR, "Error reading stopped packages: " + e);
- Log.wtf(TAG, "Error reading package manager stopped packages", e);
-
- } catch(java.io.IOException e) {
- mReadMessages.append("Error reading: " + e.toString());
- reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Log.wtf(TAG, "Error reading package manager stopped packages", e);
-
- }
- }
-
- void writeLP() {
- //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024);
-
- // Keep the old settings around until we know the new ones have
- // been successfully written.
- if (mSettingsFilename.exists()) {
- // Presence of backup settings file indicates that we failed
- // to persist settings earlier. So preserve the older
- // backup for future reference since the current settings
- // might have been corrupted.
- if (!mBackupSettingsFilename.exists()) {
- if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {
- Log.wtf(TAG, "Unable to backup package manager settings, "
- + " current changes will be lost at reboot");
- return;
- }
- } else {
- mSettingsFilename.delete();
- Slog.w(TAG, "Preserving older settings backup");
- }
- }
-
- mPastSignatures.clear();
-
- try {
- FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
- BufferedOutputStream str = new BufferedOutputStream(fstr);
-
- //XmlSerializer serializer = XmlUtils.serializerInstance();
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(str, "utf-8");
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
- serializer.startTag(null, "packages");
-
- serializer.startTag(null, "last-platform-version");
- serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform));
- serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform));
- serializer.endTag(null, "last-platform-version");
-
- serializer.startTag(null, "permission-trees");
- for (BasePermission bp : mPermissionTrees.values()) {
- writePermission(serializer, bp);
- }
- serializer.endTag(null, "permission-trees");
-
- serializer.startTag(null, "permissions");
- for (BasePermission bp : mPermissions.values()) {
- writePermission(serializer, bp);
- }
- serializer.endTag(null, "permissions");
-
- for (PackageSetting pkg : mPackages.values()) {
- writePackage(serializer, pkg);
- }
-
- for (PackageSetting pkg : mDisabledSysPackages.values()) {
- writeDisabledSysPackage(serializer, pkg);
- }
-
- serializer.startTag(null, "preferred-activities");
- for (PreferredActivity pa : mPreferredActivities.filterSet()) {
- serializer.startTag(null, "item");
- pa.writeToXml(serializer);
- serializer.endTag(null, "item");
- }
- serializer.endTag(null, "preferred-activities");
-
- for (SharedUserSetting usr : mSharedUsers.values()) {
- serializer.startTag(null, "shared-user");
- serializer.attribute(null, "name", usr.name);
- serializer.attribute(null, "userId",
- Integer.toString(usr.userId));
- usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
- serializer.startTag(null, "perms");
- for (String name : usr.grantedPermissions) {
- serializer.startTag(null, "item");
- serializer.attribute(null, "name", name);
- serializer.endTag(null, "item");
- }
- serializer.endTag(null, "perms");
- serializer.endTag(null, "shared-user");
- }
-
- if (mPackagesToBeCleaned.size() > 0) {
- for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
- serializer.startTag(null, "cleaning-package");
- serializer.attribute(null, "name", mPackagesToBeCleaned.get(i));
- serializer.endTag(null, "cleaning-package");
- }
- }
-
- if (mRenamedPackages.size() > 0) {
- for (HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) {
- serializer.startTag(null, "renamed-package");
- serializer.attribute(null, "new", e.getKey());
- serializer.attribute(null, "old", e.getValue());
- serializer.endTag(null, "renamed-package");
- }
- }
-
- serializer.endTag(null, "packages");
-
- serializer.endDocument();
-
- str.flush();
- FileUtils.sync(fstr);
- str.close();
-
- // New settings successfully written, old ones are no longer
- // needed.
- mBackupSettingsFilename.delete();
- FileUtils.setPermissions(mSettingsFilename.toString(),
- FileUtils.S_IRUSR|FileUtils.S_IWUSR
- |FileUtils.S_IRGRP|FileUtils.S_IWGRP
- |FileUtils.S_IROTH,
- -1, -1);
-
- // Write package list file now, use a JournaledFile.
- //
- File tempFile = new File(mPackageListFilename.toString() + ".tmp");
- JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
-
- fstr = new FileOutputStream(journal.chooseForWrite());
- str = new BufferedOutputStream(fstr);
- try {
- StringBuilder sb = new StringBuilder();
- for (PackageSetting pkg : mPackages.values()) {
- ApplicationInfo ai = pkg.pkg.applicationInfo;
- String dataPath = ai.dataDir;
- boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-
- // Avoid any application that has a space in its path
- // or that is handled by the system.
- if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID)
- continue;
-
- // we store on each line the following information for now:
- //
- // pkgName - package name
- // userId - application-specific user id
- // debugFlag - 0 or 1 if the package is debuggable.
- // dataPath - path to package's data path
- //
- // NOTE: We prefer not to expose all ApplicationInfo flags for now.
- //
- // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
- // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
- // system/core/run-as/run-as.c
- //
- sb.setLength(0);
- sb.append(ai.packageName);
- sb.append(" ");
- sb.append((int)ai.uid);
- sb.append(isDebug ? " 1 " : " 0 ");
- sb.append(dataPath);
- sb.append("\n");
- str.write(sb.toString().getBytes());
+ // Stale container on sdcard. Just delete
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Container : " + cid + " stale");
+ removeCids.add(cid);
}
- str.flush();
- FileUtils.sync(fstr);
- str.close();
- journal.commit();
}
- catch (Exception e) {
- journal.rollback();
- }
-
- FileUtils.setPermissions(mPackageListFilename.toString(),
- FileUtils.S_IRUSR|FileUtils.S_IWUSR
- |FileUtils.S_IRGRP|FileUtils.S_IWGRP
- |FileUtils.S_IROTH,
- -1, -1);
-
- writeStoppedLP();
-
- return;
-
- } catch(XmlPullParserException e) {
- Log.wtf(TAG, "Unable to write package manager settings, "
- + "current changes will be lost at reboot", e);
- } catch(java.io.IOException e) {
- Log.wtf(TAG, "Unable to write package manager settings, "
- + "current changes will be lost at reboot", e);
}
- // Clean up partially written files
- if (mSettingsFilename.exists()) {
- if (!mSettingsFilename.delete()) {
- Log.wtf(TAG, "Failed to clean up mangled file: " + mSettingsFilename);
- }
- }
- //Debug.stopMethodTracing();
- }
- void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg)
- throws java.io.IOException {
- serializer.startTag(null, "updated-package");
- serializer.attribute(null, "name", pkg.name);
- if (pkg.realName != null) {
- serializer.attribute(null, "realName", pkg.realName);
- }
- serializer.attribute(null, "codePath", pkg.codePathString);
- serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
- serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
- serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
- serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
- 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));
- } else {
- serializer.attribute(null, "sharedUserId",
- Integer.toString(pkg.userId));
- }
- serializer.startTag(null, "perms");
- if (pkg.sharedUser == null) {
- // If this is a shared user, the permissions will
- // be written there. We still need to write an
- // empty permissions list so permissionsFixed will
- // be set.
- for (final String name : pkg.grantedPermissions) {
- BasePermission bp = mPermissions.get(name);
- if (bp != null) {
- // We only need to write signature or system permissions but this wont
- // match the semantics of grantedPermissions. So write all permissions.
- serializer.startTag(null, "item");
- serializer.attribute(null, "name", name);
- serializer.endTag(null, "item");
+ if (num > 0) {
+ // Sort uid list
+ Arrays.sort(uidList, 0, num);
+ // Throw away duplicates
+ uidArr = new int[num];
+ uidArr[0] = uidList[0];
+ int di = 0;
+ for (int i = 1; i < num; i++) {
+ if (uidList[i - 1] != uidList[i]) {
+ uidArr[di++] = uidList[i];
}
}
}
- serializer.endTag(null, "perms");
- serializer.endTag(null, "updated-package");
}
-
- void writePackage(XmlSerializer serializer, final PackageSetting pkg)
- throws java.io.IOException {
- serializer.startTag(null, "package");
- serializer.attribute(null, "name", pkg.name);
- if (pkg.realName != null) {
- serializer.attribute(null, "realName", pkg.realName);
- }
- serializer.attribute(null, "codePath", pkg.codePathString);
- 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, "ft", Long.toHexString(pkg.timeStamp));
- serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
- serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
- serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
- if (pkg.sharedUser == null) {
- serializer.attribute(null, "userId",
- Integer.toString(pkg.userId));
- } else {
- serializer.attribute(null, "sharedUserId",
- Integer.toString(pkg.userId));
- }
- if (pkg.uidError) {
- serializer.attribute(null, "uidError", "true");
- }
- if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
- serializer.attribute(null, "enabled",
- pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED
- ? "true" : "false");
- }
- if(pkg.installStatus == PKG_INSTALL_INCOMPLETE) {
- serializer.attribute(null, "installStatus", "false");
- }
- if (pkg.installerPackageName != null) {
- serializer.attribute(null, "installer", pkg.installerPackageName);
- }
- pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
- if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
- serializer.startTag(null, "perms");
- if (pkg.sharedUser == null) {
- // If this is a shared user, the permissions will
- // be written there. We still need to write an
- // empty permissions list so permissionsFixed will
- // be set.
- for (final String name : pkg.grantedPermissions) {
- serializer.startTag(null, "item");
- serializer.attribute(null, "name", name);
- serializer.endTag(null, "item");
- }
- }
- serializer.endTag(null, "perms");
- }
- if (pkg.disabledComponents.size() > 0) {
- serializer.startTag(null, "disabled-components");
- for (final String name : pkg.disabledComponents) {
- serializer.startTag(null, "item");
- serializer.attribute(null, "name", name);
- serializer.endTag(null, "item");
- }
- serializer.endTag(null, "disabled-components");
- }
- if (pkg.enabledComponents.size() > 0) {
- serializer.startTag(null, "enabled-components");
- for (final String name : pkg.enabledComponents) {
- serializer.startTag(null, "item");
- serializer.attribute(null, "name", name);
- serializer.endTag(null, "item");
- }
- serializer.endTag(null, "enabled-components");
- }
-
- serializer.endTag(null, "package");
- }
-
- void writePermission(XmlSerializer serializer, BasePermission bp)
- throws XmlPullParserException, java.io.IOException {
- if (bp.type != BasePermission.TYPE_BUILTIN
- && bp.sourcePackage != null) {
- serializer.startTag(null, "item");
- serializer.attribute(null, "name", bp.name);
- serializer.attribute(null, "package", bp.sourcePackage);
- if (bp.protectionLevel !=
- PermissionInfo.PROTECTION_NORMAL) {
- serializer.attribute(null, "protection",
- Integer.toString(bp.protectionLevel));
- }
- if (DEBUG_SETTINGS) Log.v(TAG,
- "Writing perm: name=" + bp.name + " type=" + bp.type);
- if (bp.type == BasePermission.TYPE_DYNAMIC) {
- PermissionInfo pi = bp.perm != null ? bp.perm.info
- : bp.pendingInfo;
- if (pi != null) {
- serializer.attribute(null, "type", "dynamic");
- if (pi.icon != 0) {
- serializer.attribute(null, "icon",
- Integer.toString(pi.icon));
- }
- if (pi.nonLocalizedLabel != null) {
- serializer.attribute(null, "label",
- pi.nonLocalizedLabel.toString());
- }
- }
- }
- serializer.endTag(null, "item");
- }
- }
-
- String getReadMessagesLP() {
- return mReadMessages.toString();
- }
-
- ArrayList<PackageSetting> getListOfIncompleteInstallPackages() {
- HashSet<String> kList = new HashSet<String>(mPackages.keySet());
- Iterator<String> its = kList.iterator();
- ArrayList<PackageSetting> ret = new ArrayList<PackageSetting>();
- while(its.hasNext()) {
- String key = its.next();
- PackageSetting ps = mPackages.get(key);
- if(ps.getInstallStatus() == PKG_INSTALL_INCOMPLETE) {
- ret.add(ps);
- }
- }
- return ret;
+ // Process packages with valid entries.
+ if (mediaStatus) {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Loading packages");
+ loadMediaPackages(processCids, uidArr, removeCids);
+ startCleaningPackages();
+ } else {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Unloading packages");
+ unloadMediaPackages(processCids, uidArr, reportStatus);
}
+ }
- boolean readLP() {
- FileInputStream str = null;
- if (mBackupSettingsFilename.exists()) {
- try {
- str = new FileInputStream(mBackupSettingsFilename);
- mReadMessages.append("Reading from backup settings file\n");
- reportSettingsProblem(Log.INFO, "Need to read from backup settings file");
- if (mSettingsFilename.exists()) {
- // If both the backup and settings file exist, we
- // ignore the settings since it might have been
- // corrupted.
- Slog.w(TAG, "Cleaning up settings file " + mSettingsFilename);
- mSettingsFilename.delete();
- }
- } catch (java.io.IOException e) {
- // We'll try for the normal settings file.
- }
- }
-
- mPastSignatures.clear();
-
- try {
- if (str == null) {
- if (!mSettingsFilename.exists()) {
- mReadMessages.append("No settings file found\n");
- reportSettingsProblem(Log.INFO, "No settings file; creating initial state");
- return false;
- }
- str = new FileInputStream(mSettingsFilename);
- }
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, null);
-
- int type;
- while ((type=parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
-
- if (type != XmlPullParser.START_TAG) {
- mReadMessages.append("No start tag found in settings file\n");
- reportSettingsProblem(Log.WARN, "No start tag found in package manager settings");
- Log.wtf(TAG, "No start tag found in package manager settings");
- return false;
- }
-
- int outerDepth = parser.getDepth();
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("package")) {
- readPackageLP(parser);
- } else if (tagName.equals("permissions")) {
- readPermissionsLP(mPermissions, parser);
- } else if (tagName.equals("permission-trees")) {
- readPermissionsLP(mPermissionTrees, parser);
- } else if (tagName.equals("shared-user")) {
- readSharedUserLP(parser);
- } else if (tagName.equals("preferred-packages")) {
- // no longer used.
- } else if (tagName.equals("preferred-activities")) {
- readPreferredActivitiesLP(parser);
- } else if(tagName.equals("updated-package")) {
- readDisabledSysPackageLP(parser);
- } else if (tagName.equals("cleaning-package")) {
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- mPackagesToBeCleaned.add(name);
- }
- } else if (tagName.equals("renamed-package")) {
- String nname = parser.getAttributeValue(null, "new");
- String oname = parser.getAttributeValue(null, "old");
- if (nname != null && oname != null) {
- mRenamedPackages.put(nname, oname);
- }
- } else if (tagName.equals("last-platform-version")) {
- mInternalSdkPlatform = mExternalSdkPlatform = 0;
- try {
- String internal = parser.getAttributeValue(null, "internal");
- if (internal != null) {
- mInternalSdkPlatform = Integer.parseInt(internal);
- }
- String external = parser.getAttributeValue(null, "external");
- if (external != null) {
- mExternalSdkPlatform = Integer.parseInt(external);
- }
- } catch (NumberFormatException e) {
- }
- } else {
- Slog.w(TAG, "Unknown element under <packages>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- str.close();
-
- } catch(XmlPullParserException e) {
- mReadMessages.append("Error reading: " + e.toString());
- reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Log.wtf(TAG, "Error reading package manager settings", e);
-
- } catch(java.io.IOException e) {
- mReadMessages.append("Error reading: " + e.toString());
- reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Log.wtf(TAG, "Error reading package manager settings", e);
-
- }
-
- int N = mPendingPackages.size();
- for (int i=0; i<N; i++) {
- final PendingPackage pp = mPendingPackages.get(i);
- Object idObj = getUserIdLP(pp.sharedId);
- if (idObj != null && idObj instanceof SharedUserSetting) {
- PackageSetting p = getPackageLP(pp.name, null, pp.realName,
- (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
- pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
- if (p == null) {
- reportSettingsProblem(Log.WARN, "Unable to create application package for "
- + pp.name);
- continue;
- }
- p.copyFrom(pp);
- } else if (idObj != null) {
- String msg = "Bad package setting: package " + pp.name
- + " has shared uid " + pp.sharedId
- + " that is not a shared uid\n";
- mReadMessages.append(msg);
- reportSettingsProblem(Log.ERROR, msg);
- } else {
- String msg = "Bad package setting: package " + pp.name
- + " has shared uid " + pp.sharedId
- + " that is not defined\n";
- mReadMessages.append(msg);
- reportSettingsProblem(Log.ERROR, msg);
- }
- }
- mPendingPackages.clear();
-
- /*
- * Make sure all the updated system packages have their shared users
- * associated with them.
- */
- final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
- while (disabledIt.hasNext()) {
- final PackageSetting disabledPs = disabledIt.next();
- final Object id = getUserIdLP(disabledPs.userId);
- if (id != null && id instanceof SharedUserSetting) {
- disabledPs.sharedUser = (SharedUserSetting) id;
- }
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, ArrayList<String> pkgList,
+ int uidArr[], IIntentReceiver finishedReceiver) {
+ int size = pkgList.size();
+ if (size > 0) {
+ // Send broadcasts here
+ Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList
+ .toArray(new String[size]));
+ if (uidArr != null) {
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
}
-
- readStoppedLP();
-
- mReadMessages.append("Read completed successfully: "
- + mPackages.size() + " packages, "
- + mSharedUsers.size() + " shared uids\n");
-
- return true;
+ String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
+ : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
+ sendPackageBroadcast(action, null, extras, null, finishedReceiver);
}
+ }
- private int readInt(XmlPullParser parser, String ns, String name,
- int defValue) {
- String v = parser.getAttributeValue(ns, name);
+ /*
+ * Look at potentially valid container ids from processCids If package
+ * information doesn't match the one on record or package scanning fails,
+ * the cid is added to list of removeCids. We currently don't delete stale
+ * containers.
+ */
+ private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[],
+ HashSet<String> removeCids) {
+ ArrayList<String> pkgList = new ArrayList<String>();
+ Set<SdInstallArgs> keys = processCids.keySet();
+ boolean doGc = false;
+ for (SdInstallArgs args : keys) {
+ String codePath = processCids.get(args);
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Loading container : " + args.cid);
+ int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
try {
- if (v == null) {
- return defValue;
- }
- return Integer.parseInt(v);
- } catch (NumberFormatException e) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: attribute " +
- name + " has bad integer value " + v + " at "
- + parser.getPositionDescription());
- }
- return defValue;
- }
-
- private void readPermissionsLP(HashMap<String, BasePermission> out,
- XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
+ // Make sure there are no container errors first.
+ if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
+ Slog.e(TAG, "Failed to mount cid : " + args.cid
+ + " when installing from sdcard");
continue;
}
-
- String tagName = parser.getName();
- if (tagName.equals("item")) {
- String name = parser.getAttributeValue(null, "name");
- String sourcePackage = parser.getAttributeValue(null, "package");
- String ptype = parser.getAttributeValue(null, "type");
- if (name != null && sourcePackage != null) {
- boolean dynamic = "dynamic".equals(ptype);
- BasePermission bp = new BasePermission(name, sourcePackage,
- dynamic
- ? BasePermission.TYPE_DYNAMIC
- : BasePermission.TYPE_NORMAL);
- bp.protectionLevel = readInt(parser, null, "protection",
- PermissionInfo.PROTECTION_NORMAL);
- if (dynamic) {
- PermissionInfo pi = new PermissionInfo();
- pi.packageName = sourcePackage.intern();
- pi.name = name.intern();
- pi.icon = readInt(parser, null, "icon", 0);
- pi.nonLocalizedLabel = parser.getAttributeValue(
- null, "label");
- pi.protectionLevel = bp.protectionLevel;
- bp.pendingInfo = pi;
- }
- out.put(bp.name, bp);
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: permissions has"
- + " no name at " + parser.getPositionDescription());
- }
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element reading permissions: "
- + parser.getName() + " at "
- + parser.getPositionDescription());
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- private void readDisabledSysPackageLP(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- String name = parser.getAttributeValue(null, "name");
- String realName = parser.getAttributeValue(null, "realName");
- String codePathStr = parser.getAttributeValue(null, "codePath");
- String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
- String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
- if (resourcePathStr == null) {
- resourcePathStr = codePathStr;
- }
- String version = parser.getAttributeValue(null, "version");
- int versionCode = 0;
- if (version != null) {
- try {
- versionCode = Integer.parseInt(version);
- } catch (NumberFormatException e) {
- }
- }
-
- int pkgFlags = 0;
- pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
- new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags);
- String timeStampStr = parser.getAttributeValue(null, "ft");
- if (timeStampStr != null) {
- try {
- long timeStamp = Long.parseLong(timeStampStr, 16);
- ps.setTimeStamp(timeStamp);
- } catch (NumberFormatException e) {
- }
- } else {
- timeStampStr = parser.getAttributeValue(null, "ts");
- if (timeStampStr != null) {
- try {
- long timeStamp = Long.parseLong(timeStampStr);
- ps.setTimeStamp(timeStamp);
- } catch (NumberFormatException e) {
- }
- }
- }
- timeStampStr = parser.getAttributeValue(null, "it");
- if (timeStampStr != null) {
- try {
- ps.firstInstallTime = Long.parseLong(timeStampStr, 16);
- } catch (NumberFormatException e) {
- }
- }
- timeStampStr = parser.getAttributeValue(null, "ut");
- if (timeStampStr != null) {
- try {
- ps.lastUpdateTime = Long.parseLong(timeStampStr, 16);
- } catch (NumberFormatException e) {
- }
- }
- String idStr = parser.getAttributeValue(null, "userId");
- ps.userId = idStr != null ? Integer.parseInt(idStr) : 0;
- if(ps.userId <= 0) {
- String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
- ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
- }
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
+ // Check code path here.
+ if (codePath == null || !codePath.equals(args.getCodePath())) {
+ Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()
+ + " does not match one in settings " + codePath);
continue;
}
-
- String tagName = parser.getName();
- if (tagName.equals("perms")) {
- readGrantedPermissionsLP(parser,
- ps.grantedPermissions);
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <updated-package>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
- mDisabledSysPackages.put(name, ps);
- }
-
- private void readPackageLP(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- String name = null;
- String realName = null;
- String idStr = null;
- String sharedIdStr = null;
- String codePathStr = null;
- String resourcePathStr = null;
- String nativeLibraryPathStr = null;
- String systemStr = null;
- String installerPackageName = null;
- String uidError = null;
- int pkgFlags = 0;
- long timeStamp = 0;
- long firstInstallTime = 0;
- long lastUpdateTime = 0;
- PackageSettingBase packageSetting = null;
- String version = null;
- int versionCode = 0;
- try {
- name = parser.getAttributeValue(null, "name");
- realName = parser.getAttributeValue(null, "realName");
- idStr = parser.getAttributeValue(null, "userId");
- uidError = parser.getAttributeValue(null, "uidError");
- sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
- codePathStr = parser.getAttributeValue(null, "codePath");
- resourcePathStr = parser.getAttributeValue(null, "resourcePath");
- nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
- version = parser.getAttributeValue(null, "version");
- if (version != null) {
- try {
- versionCode = Integer.parseInt(version);
- } catch (NumberFormatException e) {
- }
- }
- installerPackageName = parser.getAttributeValue(null, "installer");
-
- systemStr = parser.getAttributeValue(null, "flags");
- if (systemStr != null) {
- try {
- pkgFlags = Integer.parseInt(systemStr);
- } catch (NumberFormatException e) {
- }
- } else {
- // For backward compatibility
- systemStr = parser.getAttributeValue(null, "system");
- if (systemStr != null) {
- pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM : 0;
- } else {
- // Old settings that don't specify system... just treat
- // them as system, good enough.
- pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- }
- }
- String timeStampStr = parser.getAttributeValue(null, "ft");
- if (timeStampStr != null) {
- try {
- timeStamp = Long.parseLong(timeStampStr, 16);
- } catch (NumberFormatException e) {
- }
- } else {
- timeStampStr = parser.getAttributeValue(null, "ts");
- if (timeStampStr != null) {
- try {
- timeStamp = Long.parseLong(timeStampStr);
- } catch (NumberFormatException e) {
+ // Parse package
+ int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+ doGc = true;
+ synchronized (mInstallLock) {
+ final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
+ 0, 0);
+ // Scan the package
+ if (pkg != null) {
+ /*
+ * TODO why is the lock being held? doPostInstall is
+ * called in other places without the lock. This needs
+ * to be straightened out.
+ */
+ // writer
+ synchronized (mPackages) {
+ retCode = PackageManager.INSTALL_SUCCEEDED;
+ pkgList.add(pkg.packageName);
+ // Post process args
+ args.doPostInstall(PackageManager.INSTALL_SUCCEEDED);
}
- }
- }
- timeStampStr = parser.getAttributeValue(null, "it");
- if (timeStampStr != null) {
- try {
- firstInstallTime = Long.parseLong(timeStampStr, 16);
- } catch (NumberFormatException e) {
- }
- }
- timeStampStr = parser.getAttributeValue(null, "ut");
- if (timeStampStr != null) {
- try {
- lastUpdateTime = Long.parseLong(timeStampStr, 16);
- } catch (NumberFormatException e) {
- }
- }
- if (DEBUG_SETTINGS) Log.v(TAG, "Reading package: " + name
- + " userId=" + idStr + " sharedUserId=" + sharedIdStr);
- int userId = idStr != null ? Integer.parseInt(idStr) : 0;
- if (resourcePathStr == null) {
- resourcePathStr = codePathStr;
- }
- if (realName != null) {
- realName = realName.intern();
- }
- if (name == null) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <package> has no name at "
- + parser.getPositionDescription());
- } else if (codePathStr == null) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <package> has no codePath at "
- + parser.getPositionDescription());
- } else if (userId > 0) {
- packageSetting = addPackageLP(name.intern(), realName, new File(codePathStr),
- new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode,
- pkgFlags);
- if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
- + ": userId=" + userId + " pkg=" + packageSetting);
- if (packageSetting == null) {
- reportSettingsProblem(Log.ERROR,
- "Failure adding uid " + userId
- + " while parsing settings at "
- + parser.getPositionDescription());
} else {
- packageSetting.setTimeStamp(timeStamp);
- packageSetting.firstInstallTime = firstInstallTime;
- packageSetting.lastUpdateTime = lastUpdateTime;
- }
- } else if (sharedIdStr != null) {
- userId = sharedIdStr != null
- ? Integer.parseInt(sharedIdStr) : 0;
- if (userId > 0) {
- packageSetting = new PendingPackage(name.intern(), realName,
- new File(codePathStr), new File(resourcePathStr),
- nativeLibraryPathStr, userId, versionCode, pkgFlags);
- packageSetting.setTimeStamp(timeStamp);
- packageSetting.firstInstallTime = firstInstallTime;
- packageSetting.lastUpdateTime = lastUpdateTime;
- mPendingPackages.add((PendingPackage) packageSetting);
- if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
- + ": sharedUserId=" + userId + " pkg="
- + packageSetting);
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: package "
- + name + " has bad sharedId " + sharedIdStr
- + " at " + parser.getPositionDescription());
- }
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: package "
- + name + " has bad userId " + idStr + " at "
- + parser.getPositionDescription());
- }
- } catch (NumberFormatException e) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: package "
- + name + " has bad userId " + idStr + " at "
- + parser.getPositionDescription());
- }
- if (packageSetting != null) {
- packageSetting.uidError = "true".equals(uidError);
- packageSetting.installerPackageName = installerPackageName;
- packageSetting.nativeLibraryPathString = nativeLibraryPathStr;
- final String enabledStr = parser.getAttributeValue(null, "enabled");
- if (enabledStr != null) {
- if (enabledStr.equalsIgnoreCase("true")) {
- packageSetting.enabled = COMPONENT_ENABLED_STATE_ENABLED;
- } else if (enabledStr.equalsIgnoreCase("false")) {
- packageSetting.enabled = COMPONENT_ENABLED_STATE_DISABLED;
- } else if (enabledStr.equalsIgnoreCase("default")) {
- packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT;
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: package "
- + name + " has bad enabled value: " + idStr
- + " at " + parser.getPositionDescription());
- }
- } else {
- packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT;
- }
- final String installStatusStr = parser.getAttributeValue(null, "installStatus");
- if (installStatusStr != null) {
- if (installStatusStr.equalsIgnoreCase("false")) {
- packageSetting.installStatus = PKG_INSTALL_INCOMPLETE;
- } else {
- packageSetting.installStatus = PKG_INSTALL_COMPLETE;
+ Slog.i(TAG, "Failed to install pkg from " + codePath + " from sdcard");
}
}
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("disabled-components")) {
- readDisabledComponentsLP(packageSetting, parser);
- } else if (tagName.equals("enabled-components")) {
- readEnabledComponentsLP(packageSetting, parser);
- } else if (tagName.equals("sigs")) {
- packageSetting.signatures.readXml(parser, mPastSignatures);
- } else if (tagName.equals("perms")) {
- readGrantedPermissionsLP(parser,
- packageSetting.grantedPermissions);
- packageSetting.permissionsFixed = true;
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <package>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
+ } finally {
+ if (retCode != PackageManager.INSTALL_SUCCEEDED) {
+ // Don't destroy container here. Wait till gc clears things
+ // up.
+ removeCids.add(args.cid);
}
- } else {
- XmlUtils.skipCurrentTag(parser);
}
}
-
- private void readDisabledComponentsLP(PackageSettingBase packageSetting,
- XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("item")) {
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- packageSetting.disabledComponents.add(name.intern());
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <disabled-components> has"
- + " no name at " + parser.getPositionDescription());
- }
+ // writer
+ synchronized (mPackages) {
+ // If the platform SDK has changed since the last time we booted,
+ // we need to re-grant app permission to catch any new ones that
+ // appear. This is really a hack, and means that apps can in some
+ // cases get permissions that the user didn't initially explicitly
+ // allow... it would be nice to have some better way to handle
+ // this situation.
+ final boolean regrantPermissions = mSettings.mExternalSdkPlatform != mSdkVersion;
+ if (regrantPermissions)
+ Slog.i(TAG, "Platform changed from " + mSettings.mExternalSdkPlatform + " to "
+ + mSdkVersion + "; regranting permissions for external storage");
+ mSettings.mExternalSdkPlatform = mSdkVersion;
+
+ // Make sure group IDs have been assigned, and any permission
+ // changes in other apps are accounted for
+ updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
+ // can downgrade to reader
+ // Persist settings
+ mSettings.writeLPr();
+ }
+ // Send a broadcast to let everyone know we are done processing
+ if (pkgList.size() > 0) {
+ sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
+ }
+ // Force gc to avoid any stale parser references that we might have.
+ if (doGc) {
+ Runtime.getRuntime().gc();
+ }
+ // List stale containers and destroy stale temporary containers.
+ if (removeCids != null) {
+ for (String cid : removeCids) {
+ if (cid.startsWith(mTempContainerPrefix)) {
+ Log.i(TAG, "Destroying stale temporary container " + cid);
+ PackageHelper.destroySdDir(cid);
} else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <disabled-components>: "
- + parser.getName());
- }
- XmlUtils.skipCurrentTag(parser);
- }
+ Log.w(TAG, "Container " + cid + " is stale");
+ }
+ }
}
+ }
- private void readEnabledComponentsLP(PackageSettingBase packageSetting,
- XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
+ /*
+ * Utility method to unload a list of specified containers
+ */
+ private void unloadAllContainers(Set<SdInstallArgs> cidArgs) {
+ // Just unmount all valid containers.
+ for (SdInstallArgs arg : cidArgs) {
+ synchronized (mInstallLock) {
+ arg.doPostDeleteLI(false);
+ }
+ }
+ }
- String tagName = parser.getName();
- if (tagName.equals("item")) {
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- packageSetting.enabledComponents.add(name.intern());
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <enabled-components> has"
- + " no name at " + parser.getPositionDescription());
- }
+ /*
+ * Unload packages mounted on external media. This involves deleting package
+ * data from internal structures, sending broadcasts about diabled packages,
+ * gc'ing to free up references, unmounting all secure containers
+ * corresponding to packages on external media, and posting a
+ * UPDATED_MEDIA_STATUS message if status has been requested. Please note
+ * that we always have to post this message if status has been requested no
+ * matter what.
+ */
+ private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[],
+ final boolean reportStatus) {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "unloading media packages");
+ ArrayList<String> pkgList = new ArrayList<String>();
+ ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
+ final Set<SdInstallArgs> keys = processCids.keySet();
+ for (SdInstallArgs args : keys) {
+ String pkgName = args.getPackageName();
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Trying to unload pkg : " + pkgName);
+ // Delete package internally
+ PackageRemovedInfo outInfo = new PackageRemovedInfo();
+ synchronized (mInstallLock) {
+ boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
+ outInfo, false);
+ if (res) {
+ pkgList.add(pkgName);
} else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <enabled-components>: "
- + parser.getName());
+ Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
+ failedList.add(args);
}
- XmlUtils.skipCurrentTag(parser);
}
}
- private void readSharedUserLP(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- String name = null;
- String idStr = null;
- int pkgFlags = 0;
- SharedUserSetting su = null;
- try {
- name = parser.getAttributeValue(null, "name");
- idStr = parser.getAttributeValue(null, "userId");
- int userId = idStr != null ? Integer.parseInt(idStr) : 0;
- if ("true".equals(parser.getAttributeValue(null, "system"))) {
- pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- }
- if (name == null) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <shared-user> has no name at "
- + parser.getPositionDescription());
- } else if (userId == 0) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: shared-user "
- + name + " has bad userId " + idStr + " at "
- + parser.getPositionDescription());
- } else {
- if ((su=addSharedUserLP(name.intern(), userId, pkgFlags)) == null) {
- reportSettingsProblem(Log.ERROR,
- "Occurred while parsing settings at "
- + parser.getPositionDescription());
- }
- }
- } catch (NumberFormatException e) {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: package "
- + name + " has bad userId " + idStr + " at "
- + parser.getPositionDescription());
- };
-
- if (su != null) {
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("sigs")) {
- su.signatures.readXml(parser, mPastSignatures);
- } else if (tagName.equals("perms")) {
- readGrantedPermissionsLP(parser, su.grantedPermissions);
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <shared-user>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
+ // reader
+ synchronized (mPackages) {
+ // We didn't update the settings after removing each package;
+ // write them now for all packages.
+ mSettings.writeLPr();
+ }
+
+ // We have to absolutely send UPDATED_MEDIA_STATUS only
+ // after confirming that all the receivers processed the ordered
+ // broadcast when packages get disabled, force a gc to clean things up.
+ // and unload all the containers.
+ if (pkgList.size() > 0) {
+ sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky) throws RemoteException {
+ Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
+ reportStatus ? 1 : 0, 1, keys);
+ mHandler.sendMessage(msg);
}
-
- } else {
- XmlUtils.skipCurrentTag(parser);
- }
+ });
+ } else {
+ Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 : 0, -1,
+ keys);
+ mHandler.sendMessage(msg);
}
+ }
- private void readGrantedPermissionsLP(XmlPullParser parser,
- HashSet<String> outPerms) throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("item")) {
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- outPerms.add(name.intern());
- } else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <perms> has"
- + " no name at " + parser.getPositionDescription());
- }
+ public void movePackage(final String packageName, final IPackageMoveObserver observer,
+ final int flags) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
+ int returnCode = PackageManager.MOVE_SUCCEEDED;
+ int currFlags = 0;
+ int newFlags = 0;
+ // reader
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
+ } else {
+ // Disable moving fwd locked apps and system packages
+ if (pkg.applicationInfo != null && isSystemApp(pkg)) {
+ Slog.w(TAG, "Cannot move system application");
+ returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
+ } 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 {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <perms>: "
- + parser.getName());
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- private void readPreferredActivitiesLP(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("item")) {
- PreferredActivity pa = new PreferredActivity(parser);
- if (pa.mPref.getParseError() == null) {
- mPreferredActivities.addFilter(pa);
+ // Find install location first
+ if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0
+ && (flags & PackageManager.MOVE_INTERNAL) != 0) {
+ Slog.w(TAG, "Ambigous flags specified for move location.");
+ returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
} else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <preferred-activity> "
- + pa.mPref.getParseError() + " at "
- + parser.getPositionDescription());
+ newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 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;
+ }
}
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <preferred-activities>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }
-
- // Returns -1 if we could not find an available UserId to assign
- private int newUserIdLP(Object obj) {
- // Let's be stupidly inefficient for now...
- final int N = mUserIds.size();
- for (int i=0; i<N; i++) {
- if (mUserIds.get(i) == null) {
- mUserIds.set(i, obj);
- return FIRST_APPLICATION_UID + i;
- }
- }
-
- // None left?
- if (N >= MAX_APPLICATION_UIDS) {
- return -1;
- }
-
- mUserIds.add(obj);
- return FIRST_APPLICATION_UID + N;
- }
-
- public PackageSetting getDisabledSystemPkg(String name) {
- synchronized(mPackages) {
- PackageSetting ps = mDisabledSysPackages.get(name);
- return ps;
- }
- }
-
- 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
- + " componentName = " + componentInfo.name);
- Log.v(TAG, "enabledComponents: "
- + Arrays.toString(packageSettings.enabledComponents.toArray()));
- Log.v(TAG, "disabledComponents: "
- + Arrays.toString(packageSettings.disabledComponents.toArray()));
- }
- if (packageSettings == null) {
- if (false) {
- Log.w(TAG, "WAITING FOR DEBUGGER");
- 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 componentInfo.enabled;
- }
- }
-
- // ------- apps on sdcard specific code -------
- static final boolean DEBUG_SD_INSTALL = false;
- private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
- private static final String SD_ENCRYPTION_ALGORITHM = "AES";
- static final int MAX_CONTAINERS = 250;
- private boolean mMediaMounted = false;
-
- private String getEncryptKey() {
- try {
- String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
- SD_ENCRYPTION_KEYSTORE_NAME);
- if (sdEncKey == null) {
- sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128,
- SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME);
- if (sdEncKey == null) {
- Slog.e(TAG, "Failed to create encryption keys");
- return null;
- }
- }
- return sdEncKey;
- } catch (NoSuchAlgorithmException nsae) {
- Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae);
- return null;
- } catch (IOException ioe) {
- Slog.e(TAG, "Failed to retrieve encryption keys with exception: "
- + ioe);
- return null;
- }
-
- }
-
- /* 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;
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+ pkg.mOperationPending = true;
}
- } catch (NumberFormatException e) {
}
}
- }
- return mTempContainerPrefix + tmpIdx;
- }
-
- /*
- * Update media status on PackageManager.
- */
- public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
- int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- throw new SecurityException("Media status can only be updated by the system");
- }
- synchronized (mPackages) {
- Log.i(TAG, "Updating external media status from " +
- (mMediaMounted ? "mounted" : "unmounted") + " to " +
- (mediaStatus ? "mounted" : "unmounted"));
- if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
- mediaStatus+", mMediaMounted=" + mMediaMounted);
- if (mediaStatus == mMediaMounted) {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
- reportStatus ? 1 : 0, -1);
- mHandler.sendMessage(msg);
- return;
- }
- mMediaMounted = mediaStatus;
- }
- // Queue up an async operation since the package installation may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- updateExternalMediaStatusInner(mediaStatus, reportStatus);
- }
- });
- }
-
- /*
- * Collect information of applications on external media, map them
- * against existing containers and update information based on current
- * mount status. Please note that we always have to report status
- * if reportStatus has been set to true especially when unloading packages.
- */
- private void updateExternalMediaStatusInner(boolean mediaStatus,
- boolean reportStatus) {
- // Collection of uids
- int uidArr[] = null;
- // Collection of stale containers
- HashSet<String> removeCids = new HashSet<String>();
- // Collection of packages on external media with valid containers.
- HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
- // Get list of secure containers.
- final String list[] = PackageHelper.getSecureContainerList();
- if (list == null || list.length == 0) {
- Log.i(TAG, "No secure containers on sdcard");
- } else {
- // Process list of secure containers and categorize them
- // as active or stale based on their package internal state.
- int uidList[] = new int[list.length];
- int num = 0;
- synchronized (mPackages) {
- for (String cid : list) {
- SdInstallArgs args = new SdInstallArgs(cid);
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Processing container " + cid);
- String pkgName = args.getPackageName();
- if (pkgName == null) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale");
- removeCids.add(cid);
- continue;
- }
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Looking for pkg : " + pkgName);
- PackageSetting ps = mSettings.mPackages.get(pkgName);
- // The package status is changed only if the code path
- // matches between settings and the container id.
- if (ps != null && ps.codePathString != null &&
- ps.codePathString.equals(args.getCodePath())) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid +
- " corresponds to pkg : " + pkgName +
- " at code path: " + ps.codePathString);
- // We do have a valid package installed on sdcard
- processCids.put(args, ps.codePathString);
- int uid = ps.userId;
- if (uid != -1) {
- uidList[num++] = uid;
- }
- } else {
- // Stale container on sdcard. Just delete
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale");
- removeCids.add(cid);
- }
- }
- }
-
- if (num > 0) {
- // Sort uid list
- Arrays.sort(uidList, 0, num);
- // Throw away duplicates
- uidArr = new int[num];
- uidArr[0] = uidList[0];
- int di = 0;
- for (int i = 1; i < num; i++) {
- if (uidList[i-1] != uidList[i]) {
- uidArr[di++] = uidList[i];
- }
- }
- }
- }
- // Process packages with valid entries.
- if (mediaStatus) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, removeCids);
- startCleaningPackages();
- } else {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
- unloadMediaPackages(processCids, uidArr, reportStatus);
- }
- }
-
- private void sendResourcesChangedBroadcast(boolean mediaStatus,
- ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
- int size = pkgList.size();
- if (size > 0) {
- // Send broadcasts here
- Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
- pkgList.toArray(new String[size]));
- if (uidArr != null) {
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
- }
- String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
- : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver);
- }
- }
-
- /*
- * Look at potentially valid container ids from processCids
- * If package information doesn't match the one on record
- * or package scanning fails, the cid is added to list of
- * removeCids. We currently don't delete stale containers.
- */
- private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
- int uidArr[], HashSet<String> removeCids) {
- ArrayList<String> pkgList = new ArrayList<String>();
- Set<SdInstallArgs> keys = processCids.keySet();
- boolean doGc = false;
- for (SdInstallArgs args : keys) {
- String codePath = processCids.get(args);
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading container : "
- + args.cid);
- int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- try {
- // Make sure there are no container errors first.
- if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED)
- != PackageManager.INSTALL_SUCCEEDED) {
- Slog.e(TAG, "Failed to mount cid : " + args.cid +
- " when installing from sdcard");
- continue;
- }
- // Check code path here.
- if (codePath == null || !codePath.equals(args.getCodePath())) {
- Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()+
- " does not match one in settings " + codePath);
- continue;
- }
- // Parse package
- int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
- doGc = true;
- synchronized (mInstallLock) {
- final PackageParser.Package pkg = scanPackageLI(new File(codePath),
- parseFlags, 0, 0);
- // Scan the package
- if (pkg != null) {
- synchronized (mPackages) {
- retCode = PackageManager.INSTALL_SUCCEEDED;
- pkgList.add(pkg.packageName);
- // Post process args
- args.doPostInstall(PackageManager.INSTALL_SUCCEEDED);
- }
- } else {
- Slog.i(TAG, "Failed to install pkg from " +
- codePath + " from sdcard");
- }
- }
- } finally {
- if (retCode != PackageManager.INSTALL_SUCCEEDED) {
- // Don't destroy container here. Wait till gc clears things up.
- removeCids.add(args.cid);
- }
- }
- }
- synchronized (mPackages) {
- // If the platform SDK has changed since the last time we booted,
- // we need to re-grant app permission to catch any new ones that
- // appear. This is really a hack, and means that apps can in some
- // cases get permissions that the user didn't initially explicitly
- // allow... it would be nice to have some better way to handle
- // this situation.
- final boolean regrantPermissions = mSettings.mExternalSdkPlatform
- != mSdkVersion;
- if (regrantPermissions) Slog.i(TAG, "Platform changed from "
- + mSettings.mExternalSdkPlatform + " to " + mSdkVersion
- + "; regranting permissions for external storage");
- mSettings.mExternalSdkPlatform = mSdkVersion;
-
- // Make sure group IDs have been assigned, and any permission
- // changes in other apps are accounted for
- updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions);
- // Persist settings
- mSettings.writeLP();
- }
- // Send a broadcast to let everyone know we are done processing
- if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
- }
- // Force gc to avoid any stale parser references that we might have.
- if (doGc) {
- Runtime.getRuntime().gc();
- }
- // List stale containers and destroy stale temporary containers.
- if (removeCids != null) {
- for (String cid : removeCids) {
- if (cid.startsWith(mTempContainerPrefix)) {
- Log.i(TAG, "Destroying stale temporary container " + cid);
- PackageHelper.destroySdDir(cid);
- } else {
- Log.w(TAG, "Container " + cid + " is stale");
- }
- }
- }
- }
-
- /*
- * Utility method to unload a list of specified containers
- */
- private void unloadAllContainers(Set<SdInstallArgs> cidArgs) {
- // Just unmount all valid containers.
- for (SdInstallArgs arg : cidArgs) {
- synchronized (mInstallLock) {
- arg.doPostDeleteLI(false);
- }
- }
- }
-
- /*
- * Unload packages mounted on external media. This involves deleting
- * package data from internal structures, sending broadcasts about
- * diabled packages, gc'ing to free up references, unmounting all
- * secure containers corresponding to packages on external media, and
- * posting a UPDATED_MEDIA_STATUS message if status has been requested.
- * Please note that we always have to post this message if status has
- * been requested no matter what.
- */
- private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids,
- int uidArr[], final boolean reportStatus) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
- ArrayList<String> pkgList = new ArrayList<String>();
- ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
- final Set<SdInstallArgs> keys = processCids.keySet();
- for (SdInstallArgs args : keys) {
- String cid = args.cid;
- String pkgName = args.getPackageName();
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName);
- // Delete package internally
- PackageRemovedInfo outInfo = new PackageRemovedInfo();
- synchronized (mInstallLock) {
- boolean res = deletePackageLI(pkgName, false,
- PackageManager.DONT_DELETE_DATA, outInfo, false);
- if (res) {
- pkgList.add(pkgName);
- } else {
- Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
- failedList.add(args);
- }
- }
- }
-
- synchronized (mPackages) {
- // We didn't update the settings after removing each package;
- // write them now for all packages.
- mSettings.writeLP();
- }
-
- // We have to absolutely send UPDATED_MEDIA_STATUS only
- // after confirming that all the receivers processed the ordered
- // broadcast when packages get disabled, force a gc to clean things up.
- // and unload all the containers.
- if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky) throws RemoteException {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
- reportStatus ? 1 : 0, 1, keys);
- mHandler.sendMessage(msg);
- }
- });
- } else {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
- reportStatus ? 1 : 0, -1, keys);
- mHandler.sendMessage(msg);
- }
- }
-
- public void movePackage(final String packageName,
- final IPackageMoveObserver observer, final int flags) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MOVE_PACKAGE, null);
- int returnCode = PackageManager.MOVE_SUCCEEDED;
- int currFlags = 0;
- int newFlags = 0;
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
- } else {
- // Disable moving fwd locked apps and system packages
- if (pkg.applicationInfo != null && isSystemApp(pkg)) {
- Slog.w(TAG, "Cannot move system application");
- returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
- } 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 &&
- (flags & PackageManager.MOVE_INTERNAL) != 0) {
- Slog.w(TAG, "Ambigous flags specified for move location.");
- returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
- } else {
- newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 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) {
+ /*
+ * TODO this next block probably shouldn't be inside the lock. We
+ * can't guarantee these won't change after this is fired off
+ * anyway.
+ */
+ if (returnCode != PackageManager.MOVE_SUCCEEDED) {
processPendingMove(new MoveParams(null, observer, 0, packageName, null), returnCode);
- } else {
- Message msg = mHandler.obtainMessage(INIT_COPY);
- InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
+ } else {
+ Message msg = mHandler.obtainMessage(INIT_COPY);
+ InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir);
- MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
+ MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
pkg.applicationInfo.dataDir);
- msg.obj = mp;
- mHandler.sendMessage(msg);
- }
- }
- }
+ msg.obj = mp;
+ mHandler.sendMessage(msg);
+ }
+ }
+ }
- private void processPendingMove(final MoveParams mp, final int currentStatus) {
- // Queue up an async operation since the package deletion may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- int returnCode = currentStatus;
- if (currentStatus == PackageManager.MOVE_SUCCEEDED) {
- int uidArr[] = null;
- ArrayList<String> pkgList = null;
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(mp.packageName);
- if (pkg == null) {
- Slog.w(TAG, " Package " + mp.packageName +
- " doesn't exist. Aborting move");
- returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
- } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
- Slog.w(TAG, "Package " + mp.packageName + " code path changed from " +
- mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir +
- " Aborting move and returning error");
- returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
- } else {
- uidArr = new int[] { pkg.applicationInfo.uid };
- pkgList = new ArrayList<String>();
- pkgList.add(mp.packageName);
- }
- }
- if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- // Send resources unavailable broadcast
- sendResourcesChangedBroadcast(false, pkgList, uidArr, null);
- // Update package code and resource paths
- synchronized (mInstallLock) {
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(mp.packageName);
- // Recheck for package again.
+ private void processPendingMove(final MoveParams mp, final int currentStatus) {
+ // Queue up an async operation since the package deletion may take a
+ // little while.
+ mHandler.post(new Runnable() {
+ public void run() {
+ // TODO fix this; this does nothing.
+ mHandler.removeCallbacks(this);
+ int returnCode = currentStatus;
+ if (currentStatus == PackageManager.MOVE_SUCCEEDED) {
+ int uidArr[] = null;
+ ArrayList<String> pkgList = null;
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ if (pkg == null) {
+ Slog.w(TAG, " Package " + mp.packageName
+ + " doesn't exist. Aborting move");
+ returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
+ } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
+ Slog.w(TAG, "Package " + mp.packageName + " code path changed from "
+ + mp.srcArgs.getCodePath() + " to "
+ + pkg.applicationInfo.sourceDir
+ + " Aborting move and returning error");
+ returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+ } else {
+ uidArr = new int[] {
+ pkg.applicationInfo.uid
+ };
+ pkgList = new ArrayList<String>();
+ pkgList.add(mp.packageName);
+ }
+ }
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+ // Send resources unavailable broadcast
+ sendResourcesChangedBroadcast(false, pkgList, uidArr, null);
+ // Update package code and resource paths
+ synchronized (mInstallLock) {
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ // Recheck for package again.
if (pkg == null) {
Slog.w(TAG, " Package " + mp.packageName
+ " doesn't exist. Aborting move");
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
- } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
- Slog.w(TAG, "Package " + mp.packageName + " code path changed from " +
- mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir +
- " Aborting move and returning error");
- returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
- } else {
- final String oldCodePath = pkg.mPath;
- final String newCodePath = mp.targetArgs.getCodePath();
- final String newResPath = mp.targetArgs.getResourcePath();
- final String newNativePath = mp.targetArgs.getNativeLibraryPath();
+ } else if (!mp.srcArgs.getCodePath().equals(
+ pkg.applicationInfo.sourceDir)) {
+ Slog.w(TAG, "Package " + mp.packageName
+ + " code path changed from " + mp.srcArgs.getCodePath()
+ + " to " + pkg.applicationInfo.sourceDir
+ + " Aborting move and returning error");
+ returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+ } else {
+ final String oldCodePath = pkg.mPath;
+ final String newCodePath = mp.targetArgs.getCodePath();
+ final String newResPath = mp.targetArgs.getResourcePath();
+ final String newNativePath = mp.targetArgs
+ .getNativeLibraryPath();
if ((mp.flags & PackageManager.INSTALL_EXTERNAL) == 0) {
if (mInstaller
.unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) {
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
} else {
- NativeLibraryHelper.copyNativeBinariesLI(
- new File(newCodePath), new File(newNativePath));
+ NativeLibraryHelper.copyNativeBinariesLI(new File(
+ newCodePath), new File(newNativePath));
}
} else {
if (mInstaller.linkNativeLibraryDirectory(
@@ -10442,90 +7962,105 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- 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;
- } else {
- pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
- }
- ps.setFlags(pkg.applicationInfo.flags);
- mAppDirs.remove(oldCodePath);
- mAppDirs.put(newCodePath, pkg);
- // Persist settings
- mSettings.writeLP();
- }
- }
- }
- }
- // Send resources available broadcast
- sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
- }
- }
- if (returnCode != PackageManager.MOVE_SUCCEEDED){
- // Clean up failed installation
- if (mp.targetArgs != null) {
- mp.targetArgs.doPostInstall(PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
- }
- } else {
- // Force a gc to clear things up.
- Runtime.getRuntime().gc();
- // Delete older code
- synchronized (mInstallLock) {
- mp.srcArgs.doPostDeleteLI(true);
- }
- }
+ 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;
+ } else {
+ pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ }
+ ps.setFlags(pkg.applicationInfo.flags);
+ mAppDirs.remove(oldCodePath);
+ mAppDirs.put(newCodePath, pkg);
+ // Persist settings
+ mSettings.writeLPr();
+ }
+ }
+ }
+ }
+ // Send resources available broadcast
+ sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
+ }
+ }
+ if (returnCode != PackageManager.MOVE_SUCCEEDED) {
+ // Clean up failed installation
+ if (mp.targetArgs != null) {
+ mp.targetArgs.doPostInstall(PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+ }
+ } else {
+ // Force a gc to clear things up.
+ Runtime.getRuntime().gc();
+ // Delete older code
+ synchronized (mInstallLock) {
+ 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;
+ // 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 {
- observer.packageMoved(mp.packageName, returnCode);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
- }
- }
- });
- }
+ IPackageMoveObserver observer = mp.observer;
+ if (observer != null) {
+ try {
+ observer.packageMoved(mp.packageName, returnCode);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+ });
+ }
- public boolean setInstallLocation(int loc) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS, null);
- if (getInstallLocation() == loc) {
- return true;
- }
- if (loc == PackageHelper.APP_INSTALL_AUTO ||
- loc == PackageHelper.APP_INSTALL_INTERNAL ||
- loc == PackageHelper.APP_INSTALL_EXTERNAL) {
- android.provider.Settings.System.putInt(mContext.getContentResolver(),
- android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, loc);
- return true;
- }
- return false;
+ public boolean setInstallLocation(int loc) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
+ null);
+ if (getInstallLocation() == loc) {
+ return true;
+ }
+ if (loc == PackageHelper.APP_INSTALL_AUTO || loc == PackageHelper.APP_INSTALL_INTERNAL
+ || loc == PackageHelper.APP_INSTALL_EXTERNAL) {
+ android.provider.Settings.System.putInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, loc);
+ return true;
+ }
+ return false;
}
- public int getInstallLocation() {
- return android.provider.Settings.System.getInt(mContext.getContentResolver(),
- android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, PackageHelper.APP_INSTALL_AUTO);
- }
+ public int getInstallLocation() {
+ return android.provider.Settings.System.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION,
+ PackageHelper.APP_INSTALL_AUTO);
+ }
+
+ public UserInfo createUser(String name, int flags) {
+ UserInfo userInfo = mUserManager.createUser(name, flags, getInstalledApplications(0));
+ return userInfo;
+ }
+
+ public boolean removeUser(int userId) {
+ if (userId == 0) {
+ return false;
+ }
+ mUserManager.removeUser(userId);
+ return true;
+ }
}
diff --git a/services/java/com/android/server/pm/PackageSetting.java b/services/java/com/android/server/pm/PackageSetting.java
new file mode 100644
index 0000000..efdc2b3
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageSetting.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.PackageParser;
+
+import java.io.File;
+
+/**
+ * Settings data for a particular package we know about.
+ */
+final class PackageSetting extends PackageSettingBase {
+ int userId;
+ PackageParser.Package pkg;
+ SharedUserSetting sharedUser;
+
+ PackageSetting(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+ pkgFlags);
+ }
+
+ /**
+ * New instance of PackageSetting replicating the original settings.
+ * Note that it keeps the same PackageParser.Package instance.
+ */
+ PackageSetting(PackageSetting orig) {
+ super(orig);
+
+ userId = orig.userId;
+ pkg = orig.pkg;
+ sharedUser = orig.sharedUser;
+ }
+
+ @Override
+ public String toString() {
+ return "PackageSetting{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + name + "/" + userId + "}";
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
new file mode 100644
index 0000000..e2f83ad
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
+
+import java.io.File;
+import java.util.HashSet;
+
+/**
+ * Settings base class for pending and resolved classes.
+ */
+class PackageSettingBase extends GrantedPermissions {
+ /**
+ * Indicates the state of installation. Used by PackageManager to figure out
+ * incomplete installations. Say a package is being installed (the state is
+ * set to PKG_INSTALL_INCOMPLETE) and remains so till the package
+ * installation is successful or unsuccessful in which case the
+ * PackageManager will no longer maintain state information associated with
+ * the package. If some exception(like device freeze or battery being pulled
+ * out) occurs during installation of a package, the PackageManager needs
+ * this information to clean up the previously failed installation.
+ */
+ static final int PKG_INSTALL_COMPLETE = 1;
+ static final int PKG_INSTALL_INCOMPLETE = 0;
+
+ final String name;
+ final String realName;
+ File codePath;
+ String codePathString;
+ File resourcePath;
+ String resourcePathString;
+ String nativeLibraryPathString;
+ long timeStamp;
+ long firstInstallTime;
+ long lastUpdateTime;
+ int versionCode;
+
+ boolean uidError;
+
+ PackageSignatures signatures = new PackageSignatures();
+
+ boolean permissionsFixed;
+ boolean haveGids;
+
+ // Whether this package is currently stopped, thus can not be
+ // started until explicitly launched by the user.
+ public boolean stopped;
+
+ // Set to true if we have never launched this app.
+ public boolean notLaunched;
+
+ /* Explicitly disabled components */
+ HashSet<String> disabledComponents = new HashSet<String>(0);
+ /* Explicitly enabled components */
+ HashSet<String> enabledComponents = new HashSet<String>(0);
+ int enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+ int installStatus = PKG_INSTALL_COMPLETE;
+
+ PackageSettingBase origPackage;
+
+ /* package name of the app that installed this package */
+ String installerPackageName;
+ PackageSettingBase(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
+ super(pkgFlags);
+ this.name = name;
+ this.realName = realName;
+ init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
+ }
+
+ /**
+ * New instance of PackageSetting with one-level-deep cloning.
+ */
+ @SuppressWarnings("unchecked")
+ PackageSettingBase(PackageSettingBase base) {
+ super(base);
+
+ name = base.name;
+ realName = base.realName;
+ codePath = base.codePath;
+ codePathString = base.codePathString;
+ resourcePath = base.resourcePath;
+ resourcePathString = base.resourcePathString;
+ nativeLibraryPathString = base.nativeLibraryPathString;
+ timeStamp = base.timeStamp;
+ firstInstallTime = base.firstInstallTime;
+ lastUpdateTime = base.lastUpdateTime;
+ versionCode = base.versionCode;
+
+ uidError = base.uidError;
+
+ signatures = new PackageSignatures(base.signatures);
+
+ permissionsFixed = base.permissionsFixed;
+ haveGids = base.haveGids;
+ stopped = base.stopped;
+ notLaunched = base.notLaunched;
+
+ disabledComponents = (HashSet<String>) base.disabledComponents.clone();
+
+ enabledComponents = (HashSet<String>) base.enabledComponents.clone();
+
+ enabled = base.enabled;
+ installStatus = base.installStatus;
+
+ origPackage = base.origPackage;
+
+ installerPackageName = base.installerPackageName;
+ }
+
+ void init(File codePath, File resourcePath, String nativeLibraryPathString,
+ int pVersionCode) {
+ this.codePath = codePath;
+ this.codePathString = codePath.toString();
+ this.resourcePath = resourcePath;
+ this.resourcePathString = resourcePath.toString();
+ this.nativeLibraryPathString = nativeLibraryPathString;
+ this.versionCode = pVersionCode;
+ }
+
+ public void setInstallerPackageName(String packageName) {
+ installerPackageName = packageName;
+ }
+
+ String getInstallerPackageName() {
+ return installerPackageName;
+ }
+
+ public void setInstallStatus(int newStatus) {
+ installStatus = newStatus;
+ }
+
+ public int getInstallStatus() {
+ return installStatus;
+ }
+
+ public void setTimeStamp(long newStamp) {
+ timeStamp = newStamp;
+ }
+
+ /**
+ * Make a shallow copy of this package settings.
+ */
+ public void copyFrom(PackageSettingBase base) {
+ grantedPermissions = base.grantedPermissions;
+ gids = base.gids;
+
+ timeStamp = base.timeStamp;
+ firstInstallTime = base.firstInstallTime;
+ lastUpdateTime = base.lastUpdateTime;
+ signatures = base.signatures;
+ permissionsFixed = base.permissionsFixed;
+ haveGids = base.haveGids;
+ stopped = base.stopped;
+ notLaunched = base.notLaunched;
+ disabledComponents = base.disabledComponents;
+ enabledComponents = base.enabledComponents;
+ enabled = base.enabled;
+ installStatus = base.installStatus;
+ }
+
+ boolean enableComponentLPw(String componentClassName) {
+ boolean changed = disabledComponents.remove(componentClassName);
+ changed |= enabledComponents.add(componentClassName);
+ return changed;
+ }
+
+ boolean disableComponentLPw(String componentClassName) {
+ boolean changed = enabledComponents.remove(componentClassName);
+ changed |= disabledComponents.add(componentClassName);
+ return changed;
+ }
+
+ boolean restoreComponentLPw(String componentClassName) {
+ boolean changed = enabledComponents.remove(componentClassName);
+ changed |= disabledComponents.remove(componentClassName);
+ return changed;
+ }
+
+ int getCurrentEnabledStateLPr(String componentName) {
+ if (enabledComponents.contains(componentName)) {
+ return COMPONENT_ENABLED_STATE_ENABLED;
+ } else if (disabledComponents.contains(componentName)) {
+ return COMPONENT_ENABLED_STATE_DISABLED;
+ } else {
+ return COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/PackageSignatures.java b/services/java/com/android/server/pm/PackageSignatures.java
new file mode 100644
index 0000000..a25ec6c
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageSignatures.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.pm.Signature;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+class PackageSignatures {
+ Signature[] mSignatures;
+
+ PackageSignatures(PackageSignatures orig) {
+ if (orig != null && orig.mSignatures != null) {
+ mSignatures = orig.mSignatures.clone();
+ }
+ }
+
+ PackageSignatures(Signature[] sigs) {
+ assignSignatures(sigs);
+ }
+
+ PackageSignatures() {
+ }
+
+ void writeXml(XmlSerializer serializer, String tagName,
+ ArrayList<Signature> pastSignatures) throws IOException {
+ if (mSignatures == null) {
+ return;
+ }
+ serializer.startTag(null, tagName);
+ serializer.attribute(null, "count",
+ Integer.toString(mSignatures.length));
+ for (int i=0; i<mSignatures.length; i++) {
+ serializer.startTag(null, "cert");
+ final Signature sig = mSignatures[i];
+ final int sigHash = sig.hashCode();
+ final int numPast = pastSignatures.size();
+ int j;
+ for (j=0; j<numPast; j++) {
+ Signature pastSig = pastSignatures.get(j);
+ if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) {
+ serializer.attribute(null, "index", Integer.toString(j));
+ break;
+ }
+ }
+ if (j >= numPast) {
+ pastSignatures.add(sig);
+ serializer.attribute(null, "index", Integer.toString(numPast));
+ serializer.attribute(null, "key", sig.toCharsString());
+ }
+ serializer.endTag(null, "cert");
+ }
+ serializer.endTag(null, tagName);
+ }
+
+ void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures)
+ throws IOException, XmlPullParserException {
+ String countStr = parser.getAttributeValue(null, "count");
+ if (countStr == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <signatures> has"
+ + " no count at " + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ final int count = Integer.parseInt(countStr);
+ mSignatures = new Signature[count];
+ int pos = 0;
+
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("cert")) {
+ if (pos < count) {
+ String index = parser.getAttributeValue(null, "index");
+ if (index != null) {
+ try {
+ int idx = Integer.parseInt(index);
+ String key = parser.getAttributeValue(null, "key");
+ if (key == null) {
+ if (idx >= 0 && idx < pastSignatures.size()) {
+ Signature sig = pastSignatures.get(idx);
+ if (sig != null) {
+ mSignatures[pos] = pastSignatures.get(idx);
+ pos++;
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> "
+ + "index " + index + " is not defined at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> "
+ + "index " + index + " is out of bounds at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ while (pastSignatures.size() <= idx) {
+ pastSignatures.add(null);
+ }
+ Signature sig = new Signature(key);
+ pastSignatures.set(idx, sig);
+ mSignatures[pos] = sig;
+ pos++;
+ }
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> "
+ + "index " + index + " is not a number at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> has"
+ + " no index at " + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: too "
+ + "many <cert> tags, expected " + count
+ + " at " + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <cert>: "
+ + parser.getName());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+
+ if (pos < count) {
+ // Should never happen -- there is an error in the written
+ // settings -- but if it does we don't want to generate
+ // a bad array.
+ Signature[] newSigs = new Signature[pos];
+ System.arraycopy(mSignatures, 0, newSigs, 0, pos);
+ mSignatures = newSigs;
+ }
+ }
+
+ void assignSignatures(Signature[] sigs) {
+ if (sigs == null) {
+ mSignatures = null;
+ return;
+ }
+ mSignatures = new Signature[sigs.length];
+ for (int i=0; i<sigs.length; i++) {
+ mSignatures[i] = sigs[i];
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer(128);
+ buf.append("PackageSignatures{");
+ buf.append(Integer.toHexString(System.identityHashCode(this)));
+ buf.append(" [");
+ if (mSignatures != null) {
+ for (int i=0; i<mSignatures.length; i++) {
+ if (i > 0) buf.append(", ");
+ buf.append(Integer.toHexString(
+ System.identityHashCode(mSignatures[i])));
+ }
+ }
+ buf.append("]}");
+ return buf.toString();
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/PendingPackage.java b/services/java/com/android/server/pm/PendingPackage.java
new file mode 100644
index 0000000..c17cc46
--- /dev/null
+++ b/services/java/com/android/server/pm/PendingPackage.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import java.io.File;
+
+final class PendingPackage extends PackageSettingBase {
+ final int sharedId;
+
+ PendingPackage(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+ pkgFlags);
+ this.sharedId = sharedId;
+ }
+}
diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java
new file mode 100644
index 0000000..b100eb1
--- /dev/null
+++ b/services/java/com/android/server/pm/PreferredActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.internal.util.XmlUtils;
+import com.android.server.PreferredComponent;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.ComponentName;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import java.io.IOException;
+
+class PreferredActivity extends IntentFilter implements PreferredComponent.Callbacks {
+ private static final String TAG = "PreferredActivity";
+
+ private static final boolean DEBUG_FILTERS = false;
+
+ final PreferredComponent mPref;
+
+ PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
+ super(filter);
+ mPref = new PreferredComponent(this, match, set, activity);
+ }
+
+ PreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException {
+ mPref = new PreferredComponent(this, parser);
+ }
+
+ public void writeToXml(XmlSerializer serializer) throws IOException {
+ mPref.writeToXml(serializer);
+ serializer.startTag(null, "filter");
+ super.writeToXml(serializer);
+ serializer.endTag(null, "filter");
+ }
+
+ public boolean onReadTag(String tagName, XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ if (tagName.equals("filter")) {
+ if (DEBUG_FILTERS) {
+ Log.i(TAG, "Starting to parse filter...");
+ }
+ readFromXml(parser);
+ if (DEBUG_FILTERS) {
+ Log.i(TAG, "Finished filter: depth=" + parser.getDepth() + " tag="
+ + parser.getName());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <preferred-activities>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
new file mode 100644
index 0000000..2720bf8
--- /dev/null
+++ b/services/java/com/android/server/pm/Settings.java
@@ -0,0 +1,2230 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
+import com.android.internal.util.XmlUtils;
+import com.android.server.IntentResolver;
+import com.android.server.pm.PackageManagerService.DumpState;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionInfo;
+import android.content.pm.Signature;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Process;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * Holds information about dynamic settings.
+ */
+final class Settings {
+ private static final String TAG = "PackageSettings";
+
+ private static final boolean DEBUG_STOPPED = false;
+
+ private final File mSettingsFilename;
+ private final File mBackupSettingsFilename;
+ private final File mPackageListFilename;
+ private final File mStoppedPackagesFilename;
+ private final File mBackupStoppedPackagesFilename;
+ final HashMap<String, PackageSetting> mPackages =
+ new HashMap<String, PackageSetting>();
+ // List of replaced system applications
+ final HashMap<String, PackageSetting> mDisabledSysPackages =
+ new HashMap<String, PackageSetting>();
+
+ // These are the last platform API version we were using for
+ // the apps installed on internal and external storage. It is
+ // used to grant newer permissions one time during a system upgrade.
+ int mInternalSdkPlatform;
+ int mExternalSdkPlatform;
+
+ // The user's preferred activities associated with particular intent
+ // filters.
+ final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
+ new IntentResolver<PreferredActivity, PreferredActivity>() {
+ @Override
+ protected String packageForFilter(PreferredActivity filter) {
+ return filter.mPref.mComponent.getPackageName();
+ }
+ @Override
+ protected void dumpFilter(PrintWriter out, String prefix,
+ PreferredActivity filter) {
+ filter.mPref.dump(out, prefix, filter);
+ }
+ };
+ final HashMap<String, SharedUserSetting> mSharedUsers =
+ new HashMap<String, SharedUserSetting>();
+ private final ArrayList<Object> mUserIds = new ArrayList<Object>();
+ private final SparseArray<Object> mOtherUserIds =
+ new SparseArray<Object>();
+
+ // For reading/writing settings file.
+ private final ArrayList<Signature> mPastSignatures =
+ new ArrayList<Signature>();
+
+ // Mapping from permission names to info about them.
+ final HashMap<String, BasePermission> mPermissions =
+ new HashMap<String, BasePermission>();
+
+ // Mapping from permission tree names to info about them.
+ final HashMap<String, BasePermission> mPermissionTrees =
+ new HashMap<String, BasePermission>();
+
+ // Packages that have been uninstalled and still need their external
+ // storage data deleted.
+ final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+
+ // Packages that have been renamed since they were first installed.
+ // Keys are the new names of the packages, values are the original
+ // names. The packages appear everwhere else under their original
+ // names.
+ final HashMap<String, String> mRenamedPackages = new HashMap<String, String>();
+
+ final StringBuilder mReadMessages = new StringBuilder();
+
+ /**
+ * Used to track packages that have a shared user ID that hasn't been read
+ * in yet.
+ * <p>
+ * TODO: make this just a local variable that is passed in during package
+ * scanning to make it less confusing.
+ */
+ private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>();
+
+ Settings() {
+ File dataDir = Environment.getDataDirectory();
+ File systemDir = new File(dataDir, "system");
+ systemDir.mkdirs();
+ FileUtils.setPermissions(systemDir.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");
+ mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml");
+ mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml");
+ }
+
+ PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
+ String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
+ String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
+ final String name = pkg.packageName;
+ PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
+ resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
+ return p;
+ }
+
+ PackageSetting peekPackageLPr(String name) {
+ return mPackages.get(name);
+ }
+
+ void setInstallStatus(String pkgName, int status) {
+ PackageSetting p = mPackages.get(pkgName);
+ if(p != null) {
+ if(p.getInstallStatus() != status) {
+ p.setInstallStatus(status);
+ }
+ }
+ }
+
+ void setInstallerPackageName(String pkgName,
+ String installerPkgName) {
+ PackageSetting p = mPackages.get(pkgName);
+ if(p != null) {
+ p.setInstallerPackageName(installerPkgName);
+ }
+ }
+
+ SharedUserSetting getSharedUserLPw(String name,
+ int pkgFlags, boolean create) {
+ SharedUserSetting s = mSharedUsers.get(name);
+ if (s == null) {
+ if (!create) {
+ return null;
+ }
+ s = new SharedUserSetting(name, pkgFlags);
+ if (PackageManagerService.MULTIPLE_APPLICATION_UIDS) {
+ s.userId = newUserIdLPw(s);
+ } else {
+ s.userId = PackageManagerService.FIRST_APPLICATION_UID;
+ }
+ Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId);
+ // < 0 means we couldn't assign a userid; fall out and return
+ // s, which is currently null
+ if (s.userId >= 0) {
+ mSharedUsers.put(name, s);
+ }
+ }
+
+ return s;
+ }
+
+ boolean disableSystemPackageLPw(String name) {
+ final PackageSetting p = mPackages.get(name);
+ if(p == null) {
+ Log.w(PackageManagerService.TAG, "Package:"+name+" is not an installed package");
+ return false;
+ }
+ final PackageSetting dp = mDisabledSysPackages.get(name);
+ // always make sure the system package code and resource paths dont change
+ if (dp == null) {
+ if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
+ p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ }
+ mDisabledSysPackages.put(name, p);
+
+ // a little trick... when we install the new package, we don't
+ // want to modify the existing PackageSetting for the built-in
+ // version. so at this point we need a new PackageSetting that
+ // is okay to muck with.
+ PackageSetting newp = new PackageSetting(p);
+ replacePackageLPw(name, newp);
+ return true;
+ }
+ return false;
+ }
+
+ PackageSetting enableSystemPackageLPw(String name) {
+ PackageSetting p = mDisabledSysPackages.get(name);
+ if(p == null) {
+ Log.w(PackageManagerService.TAG, "Package:"+name+" is not disabled");
+ return null;
+ }
+ // Reset flag in ApplicationInfo object
+ if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
+ p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ }
+ PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
+ p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags);
+ mDisabledSysPackages.remove(name);
+ return ret;
+ }
+
+ PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
+ PackageSetting p = mPackages.get(name);
+ if (p != null) {
+ if (p.userId == uid) {
+ return p;
+ }
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Adding duplicate package, keeping first: " + name);
+ return null;
+ }
+ p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
+ vc, pkgFlags);
+ p.userId = uid;
+ if (addUserIdLPw(uid, p, name)) {
+ mPackages.put(name, p);
+ return p;
+ }
+ return null;
+ }
+
+ SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
+ SharedUserSetting s = mSharedUsers.get(name);
+ if (s != null) {
+ if (s.userId == uid) {
+ return s;
+ }
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Adding duplicate shared user, keeping first: " + name);
+ return null;
+ }
+ s = new SharedUserSetting(name, pkgFlags);
+ s.userId = uid;
+ if (addUserIdLPw(uid, s, name)) {
+ mSharedUsers.put(name, s);
+ return s;
+ }
+ return null;
+ }
+
+ // Transfer ownership of permissions from one package to another.
+ void transferPermissionsLPw(String origPkg, String newPkg) {
+ // Transfer ownership of permissions to the new package.
+ for (int i=0; i<2; i++) {
+ HashMap<String, BasePermission> permissions =
+ i == 0 ? mPermissionTrees : mPermissions;
+ for (BasePermission bp : permissions.values()) {
+ if (origPkg.equals(bp.sourcePackage)) {
+ if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG,
+ "Moving permission " + bp.name
+ + " from pkg " + bp.sourcePackage
+ + " to " + newPkg);
+ bp.sourcePackage = newPkg;
+ bp.packageSetting = null;
+ bp.perm = null;
+ if (bp.pendingInfo != null) {
+ bp.pendingInfo.packageName = newPkg;
+ }
+ bp.uid = 0;
+ bp.gids = null;
+ }
+ }
+ }
+ }
+
+ private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
+ String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
+ String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
+ PackageSetting p = mPackages.get(name);
+ if (p != null) {
+ if (!p.codePath.equals(codePath)) {
+ // Check to see if its a disabled system app
+ if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // This is an updated system app with versions in both system
+ // and data partition. Just let the most recent version
+ // take precedence.
+ Slog.w(PackageManagerService.TAG, "Trying to update system app code path from " +
+ p.codePathString + " to " + codePath.toString());
+ } else {
+ // Just a change in the code path is not an issue, but
+ // let's log a message about it.
+ Slog.i(PackageManagerService.TAG, "Package " + name + " codePath changed from " + p.codePath
+ + " to " + codePath + "; Retaining data and using new");
+ /*
+ * Since we've changed paths, we need to prefer the new
+ * native library path over the one stored in the
+ * package settings since we might have moved from
+ * internal to external storage or vice versa.
+ */
+ p.nativeLibraryPathString = nativeLibraryPathString;
+ }
+ }
+ if (p.sharedUser != sharedUser) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + name + " shared user changed from "
+ + (p.sharedUser != null ? p.sharedUser.name : "<nothing>")
+ + " to "
+ + (sharedUser != null ? sharedUser.name : "<nothing>")
+ + "; replacing with new");
+ p = null;
+ } else {
+ if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // If what we are scanning is a system package, then
+ // make it so, regardless of whether it was previously
+ // installed only in the data partition.
+ p.pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ }
+ }
+ if (p == null) {
+ // Create a new PackageSettings entry. this can end up here because
+ // of code path mismatch or user id mismatch of an updated system partition
+ if (!create) {
+ return null;
+ }
+ if (origPackage != null) {
+ // We are consuming the data from an existing package.
+ p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
+ nativeLibraryPathString, vc, pkgFlags);
+ if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + name
+ + " is adopting original package " + origPackage.name);
+ // Note that we will retain the new package's signature so
+ // that we can keep its data.
+ PackageSignatures s = p.signatures;
+ p.copyFrom(origPackage);
+ p.signatures = s;
+ p.sharedUser = origPackage.sharedUser;
+ p.userId = origPackage.userId;
+ p.origPackage = origPackage;
+ mRenamedPackages.put(name, origPackage.name);
+ name = origPackage.name;
+ // Update new package state.
+ p.setTimeStamp(codePath.lastModified());
+ } else {
+ p = new PackageSetting(name, realName, codePath, resourcePath,
+ nativeLibraryPathString, vc, pkgFlags);
+ p.setTimeStamp(codePath.lastModified());
+ p.sharedUser = sharedUser;
+ // If this is not a system app, it starts out stopped.
+ if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+ if (DEBUG_STOPPED) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Slog.i(PackageManagerService.TAG, "Stopping package " + name, e);
+ }
+ p.stopped = true;
+ p.notLaunched = true;
+ }
+ if (sharedUser != null) {
+ p.userId = sharedUser.userId;
+ } else if (PackageManagerService.MULTIPLE_APPLICATION_UIDS) {
+ // Clone the setting here for disabled system packages
+ PackageSetting dis = mDisabledSysPackages.get(name);
+ if (dis != null) {
+ // For disabled packages a new setting is created
+ // from the existing user id. This still has to be
+ // added to list of user id's
+ // Copy signatures from previous setting
+ if (dis.signatures.mSignatures != null) {
+ p.signatures.mSignatures = dis.signatures.mSignatures.clone();
+ }
+ p.userId = dis.userId;
+ // Clone permissions
+ p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
+ // Clone component info
+ p.disabledComponents = new HashSet<String>(dis.disabledComponents);
+ p.enabledComponents = new HashSet<String>(dis.enabledComponents);
+ // Add new setting to list of user ids
+ addUserIdLPw(p.userId, p, name);
+ } else {
+ // Assign new user id
+ p.userId = newUserIdLPw(p);
+ }
+ } else {
+ p.userId = PackageManagerService.FIRST_APPLICATION_UID;
+ }
+ }
+ if (p.userId < 0) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + name + " could not be assigned a valid uid");
+ return null;
+ }
+ if (add) {
+ // Finish adding new package by adding it and updating shared
+ // user preferences
+ addPackageSettingLPw(p, name, sharedUser);
+ }
+ }
+ return p;
+ }
+
+ void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) {
+ p.pkg = pkg;
+ pkg.mSetEnabled = p.enabled;
+ pkg.mSetStopped = p.stopped;
+ final String codePath = pkg.applicationInfo.sourceDir;
+ final String resourcePath = pkg.applicationInfo.publicSourceDir;
+ // Update code path if needed
+ if (!codePath.equalsIgnoreCase(p.codePathString)) {
+ Slog.w(PackageManagerService.TAG, "Code path for pkg : " + p.pkg.packageName +
+ " changing from " + p.codePathString + " to " + codePath);
+ p.codePath = new File(codePath);
+ p.codePathString = codePath;
+ }
+ //Update resource path if needed
+ if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) {
+ Slog.w(PackageManagerService.TAG, "Resource path for pkg : " + p.pkg.packageName +
+ " changing from " + p.resourcePathString + " to " + resourcePath);
+ 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;
+ }
+ // Update signatures if needed.
+ if (p.signatures.mSignatures == null) {
+ p.signatures.assignSignatures(pkg.mSignatures);
+ }
+ // If this app defines a shared user id initialize
+ // the shared user signatures as well.
+ if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
+ p.sharedUser.signatures.assignSignatures(pkg.mSignatures);
+ }
+ addPackageSettingLPw(p, pkg.packageName, p.sharedUser);
+ }
+
+ // Utility method that adds a PackageSetting to mPackages and
+ // completes updating the shared user attributes
+ private void addPackageSettingLPw(PackageSetting p, String name,
+ SharedUserSetting sharedUser) {
+ mPackages.put(name, p);
+ if (sharedUser != null) {
+ if (p.sharedUser != null && p.sharedUser != sharedUser) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Package " + p.name + " was user "
+ + p.sharedUser + " but is now " + sharedUser
+ + "; I am not changing its files so it will probably fail!");
+ p.sharedUser.packages.remove(p);
+ } else if (p.userId != sharedUser.userId) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Package " + p.name + " was user id " + p.userId
+ + " but is now user " + sharedUser
+ + " with id " + sharedUser.userId
+ + "; I am not changing its files so it will probably fail!");
+ }
+
+ sharedUser.packages.add(p);
+ p.sharedUser = sharedUser;
+ p.userId = sharedUser.userId;
+ }
+ }
+
+ /*
+ * Update the shared user setting when a package using
+ * specifying the shared user id is removed. The gids
+ * associated with each permission of the deleted package
+ * are removed from the shared user's gid list only if its
+ * not in use by other permissions of packages in the
+ * shared user setting.
+ */
+ void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) {
+ if ((deletedPs == null) || (deletedPs.pkg == null)) {
+ Slog.i(PackageManagerService.TAG,
+ "Trying to update info for null package. Just ignoring");
+ return;
+ }
+ // No sharedUserId
+ if (deletedPs.sharedUser == null) {
+ return;
+ }
+ SharedUserSetting sus = deletedPs.sharedUser;
+ // Update permissions
+ for (String eachPerm : deletedPs.pkg.requestedPermissions) {
+ boolean used = false;
+ if (!sus.grantedPermissions.contains(eachPerm)) {
+ continue;
+ }
+ for (PackageSetting pkg:sus.packages) {
+ if (pkg.pkg != null &&
+ !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
+ pkg.pkg.requestedPermissions.contains(eachPerm)) {
+ used = true;
+ break;
+ }
+ }
+ if (!used) {
+ // can safely delete this permission from list
+ sus.grantedPermissions.remove(eachPerm);
+ }
+ }
+ // Update gids
+ int newGids[] = globalGids;
+ for (String eachPerm : sus.grantedPermissions) {
+ BasePermission bp = mPermissions.get(eachPerm);
+ if (bp != null) {
+ newGids = PackageManagerService.appendInts(newGids, bp.gids);
+ }
+ }
+ sus.gids = newGids;
+ }
+
+ int removePackageLPw(String name) {
+ final PackageSetting p = mPackages.get(name);
+ if (p != null) {
+ mPackages.remove(name);
+ if (p.sharedUser != null) {
+ p.sharedUser.packages.remove(p);
+ if (p.sharedUser.packages.size() == 0) {
+ mSharedUsers.remove(p.sharedUser.name);
+ removeUserIdLPw(p.sharedUser.userId);
+ return p.sharedUser.userId;
+ }
+ } else {
+ removeUserIdLPw(p.userId);
+ return p.userId;
+ }
+ }
+ return -1;
+ }
+
+ private void replacePackageLPw(String name, PackageSetting newp) {
+ final PackageSetting p = mPackages.get(name);
+ if (p != null) {
+ if (p.sharedUser != null) {
+ p.sharedUser.packages.remove(p);
+ p.sharedUser.packages.add(newp);
+ } else {
+ replaceUserIdLPw(p.userId, newp);
+ }
+ }
+ mPackages.put(name, newp);
+ }
+
+ private boolean addUserIdLPw(int uid, Object obj, Object name) {
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) {
+ return false;
+ }
+
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+ int N = mUserIds.size();
+ final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+ while (index >= N) {
+ mUserIds.add(null);
+ N++;
+ }
+ if (mUserIds.get(index) != null) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Adding duplicate user id: " + uid
+ + " name=" + name);
+ return false;
+ }
+ mUserIds.set(index, obj);
+ } else {
+ if (mOtherUserIds.get(uid) != null) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Adding duplicate shared id: " + uid
+ + " name=" + name);
+ return false;
+ }
+ mOtherUserIds.put(uid, obj);
+ }
+ return true;
+ }
+
+ public Object getUserIdLPr(int uid) {
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+ final int N = mUserIds.size();
+ final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+ return index < N ? mUserIds.get(index) : null;
+ } else {
+ return mOtherUserIds.get(uid);
+ }
+ }
+
+ private void removeUserIdLPw(int uid) {
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+ final int N = mUserIds.size();
+ final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+ if (index < N) mUserIds.set(index, null);
+ } else {
+ mOtherUserIds.remove(uid);
+ }
+ }
+
+ private void replaceUserIdLPw(int uid, Object obj) {
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+ final int N = mUserIds.size();
+ final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+ if (index < N) mUserIds.set(index, obj);
+ } else {
+ mOtherUserIds.put(uid, obj);
+ }
+ }
+
+ void writeStoppedLPr() {
+ // Keep the old stopped packages around until we know the new ones have
+ // been successfully written.
+ if (mStoppedPackagesFilename.exists()) {
+ // Presence of backup settings file indicates that we failed
+ // to persist packages earlier. So preserve the older
+ // backup for future reference since the current packages
+ // might have been corrupted.
+ if (!mBackupStoppedPackagesFilename.exists()) {
+ if (!mStoppedPackagesFilename.renameTo(mBackupStoppedPackagesFilename)) {
+ Log.wtf(PackageManagerService.TAG, "Unable to backup package manager stopped packages, "
+ + "current changes will be lost at reboot");
+ return;
+ }
+ } else {
+ mStoppedPackagesFilename.delete();
+ Slog.w(PackageManagerService.TAG, "Preserving older stopped packages backup");
+ }
+ }
+
+ try {
+ final FileOutputStream fstr = new FileOutputStream(mStoppedPackagesFilename);
+ final BufferedOutputStream str = new BufferedOutputStream(fstr);
+
+ //XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(str, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, "stopped-packages");
+
+ for (final PackageSetting pkg : mPackages.values()) {
+ if (pkg.stopped) {
+ serializer.startTag(null, "pkg");
+ serializer.attribute(null, "name", pkg.name);
+ if (pkg.notLaunched) {
+ serializer.attribute(null, "nl", "1");
+ }
+ serializer.endTag(null, "pkg");
+ }
+ }
+
+ serializer.endTag(null, "stopped-packages");
+
+ serializer.endDocument();
+
+ str.flush();
+ FileUtils.sync(fstr);
+ str.close();
+
+ // New settings successfully written, old ones are no longer
+ // needed.
+ mBackupStoppedPackagesFilename.delete();
+ FileUtils.setPermissions(mStoppedPackagesFilename.toString(),
+ FileUtils.S_IRUSR|FileUtils.S_IWUSR
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP
+ |FileUtils.S_IROTH,
+ -1, -1);
+
+ // Done, all is good!
+ return;
+ } catch(java.io.IOException e) {
+ Log.wtf(PackageManagerService.TAG, "Unable to write package manager stopped packages, "
+ + " current changes will be lost at reboot", e);
+ }
+
+ // Clean up partially written files
+ if (mStoppedPackagesFilename.exists()) {
+ if (!mStoppedPackagesFilename.delete()) {
+ Log.i(PackageManagerService.TAG, "Failed to clean up mangled file: " + mStoppedPackagesFilename);
+ }
+ }
+ }
+
+ // Note: assumed "stopped" field is already cleared in all packages.
+ void readStoppedLPw() {
+ FileInputStream str = null;
+ if (mBackupStoppedPackagesFilename.exists()) {
+ try {
+ str = new FileInputStream(mBackupStoppedPackagesFilename);
+ mReadMessages.append("Reading from backup stopped packages file\n");
+ PackageManagerService.reportSettingsProblem(Log.INFO, "Need to read from backup stopped packages file");
+ if (mSettingsFilename.exists()) {
+ // If both the backup and normal file exist, we
+ // ignore the normal one since it might have been
+ // corrupted.
+ Slog.w(PackageManagerService.TAG, "Cleaning up stopped packages file "
+ + mStoppedPackagesFilename);
+ mStoppedPackagesFilename.delete();
+ }
+ } catch (java.io.IOException e) {
+ // We'll try for the normal settings file.
+ }
+ }
+
+ try {
+ if (str == null) {
+ if (!mStoppedPackagesFilename.exists()) {
+ mReadMessages.append("No stopped packages file found\n");
+ PackageManagerService.reportSettingsProblem(Log.INFO, "No stopped packages file file; "
+ + "assuming all started");
+ // At first boot, make sure no packages are stopped.
+ // We usually want to have third party apps initialize
+ // in the stopped state, but not at first boot.
+ for (PackageSetting pkg : mPackages.values()) {
+ pkg.stopped = false;
+ pkg.notLaunched = false;
+ }
+ return;
+ }
+ str = new FileInputStream(mStoppedPackagesFilename);
+ }
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(str, null);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ mReadMessages.append("No start tag found in stopped packages file\n");
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "No start tag found in package manager stopped packages");
+ return;
+ }
+
+ int outerDepth = parser.getDepth();
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("pkg")) {
+ String name = parser.getAttributeValue(null, "name");
+ PackageSetting ps = mPackages.get(name);
+ if (ps != null) {
+ ps.stopped = true;
+ if ("1".equals(parser.getAttributeValue(null, "nl"))) {
+ ps.notLaunched = true;
+ }
+ } else {
+ Slog.w(PackageManagerService.TAG, "No package known for stopped package: " + name);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } else {
+ Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ str.close();
+
+ } catch(XmlPullParserException e) {
+ mReadMessages.append("Error reading: " + e.toString());
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading stopped packages: " + e);
+ Log.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages", e);
+
+ } catch(java.io.IOException e) {
+ mReadMessages.append("Error reading: " + e.toString());
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
+ Log.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages", e);
+
+ }
+ }
+
+ void writeLPr() {
+ //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024);
+
+ // Keep the old settings around until we know the new ones have
+ // been successfully written.
+ if (mSettingsFilename.exists()) {
+ // Presence of backup settings file indicates that we failed
+ // to persist settings earlier. So preserve the older
+ // backup for future reference since the current settings
+ // might have been corrupted.
+ if (!mBackupSettingsFilename.exists()) {
+ if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {
+ Log.wtf(PackageManagerService.TAG, "Unable to backup package manager settings, "
+ + " current changes will be lost at reboot");
+ return;
+ }
+ } else {
+ mSettingsFilename.delete();
+ Slog.w(PackageManagerService.TAG, "Preserving older settings backup");
+ }
+ }
+
+ mPastSignatures.clear();
+
+ try {
+ FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
+ BufferedOutputStream str = new BufferedOutputStream(fstr);
+
+ //XmlSerializer serializer = XmlUtils.serializerInstance();
+ XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(str, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, "packages");
+
+ serializer.startTag(null, "last-platform-version");
+ serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform));
+ serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform));
+ serializer.endTag(null, "last-platform-version");
+
+ serializer.startTag(null, "permission-trees");
+ for (BasePermission bp : mPermissionTrees.values()) {
+ writePermissionLPr(serializer, bp);
+ }
+ serializer.endTag(null, "permission-trees");
+
+ serializer.startTag(null, "permissions");
+ for (BasePermission bp : mPermissions.values()) {
+ writePermissionLPr(serializer, bp);
+ }
+ serializer.endTag(null, "permissions");
+
+ for (final PackageSetting pkg : mPackages.values()) {
+ writePackageLPr(serializer, pkg);
+ }
+
+ for (final PackageSetting pkg : mDisabledSysPackages.values()) {
+ writeDisabledSysPackageLPr(serializer, pkg);
+ }
+
+ serializer.startTag(null, "preferred-activities");
+ for (final PreferredActivity pa : mPreferredActivities.filterSet()) {
+ serializer.startTag(null, "item");
+ pa.writeToXml(serializer);
+ serializer.endTag(null, "item");
+ }
+ serializer.endTag(null, "preferred-activities");
+
+ for (final SharedUserSetting usr : mSharedUsers.values()) {
+ serializer.startTag(null, "shared-user");
+ serializer.attribute(null, "name", usr.name);
+ serializer.attribute(null, "userId",
+ Integer.toString(usr.userId));
+ usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
+ serializer.startTag(null, "perms");
+ for (String name : usr.grantedPermissions) {
+ serializer.startTag(null, "item");
+ serializer.attribute(null, "name", name);
+ serializer.endTag(null, "item");
+ }
+ serializer.endTag(null, "perms");
+ serializer.endTag(null, "shared-user");
+ }
+
+ if (mPackagesToBeCleaned.size() > 0) {
+ for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
+ serializer.startTag(null, "cleaning-package");
+ serializer.attribute(null, "name", mPackagesToBeCleaned.get(i));
+ serializer.endTag(null, "cleaning-package");
+ }
+ }
+
+ if (mRenamedPackages.size() > 0) {
+ for (HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) {
+ serializer.startTag(null, "renamed-package");
+ serializer.attribute(null, "new", e.getKey());
+ serializer.attribute(null, "old", e.getValue());
+ serializer.endTag(null, "renamed-package");
+ }
+ }
+
+ serializer.endTag(null, "packages");
+
+ serializer.endDocument();
+
+ str.flush();
+ FileUtils.sync(fstr);
+ str.close();
+
+ // New settings successfully written, old ones are no longer
+ // needed.
+ mBackupSettingsFilename.delete();
+ FileUtils.setPermissions(mSettingsFilename.toString(),
+ FileUtils.S_IRUSR|FileUtils.S_IWUSR
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP
+ |FileUtils.S_IROTH,
+ -1, -1);
+
+ // Write package list file now, use a JournaledFile.
+ //
+ File tempFile = new File(mPackageListFilename.toString() + ".tmp");
+ JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
+
+ fstr = new FileOutputStream(journal.chooseForWrite());
+ str = new BufferedOutputStream(fstr);
+ try {
+ StringBuilder sb = new StringBuilder();
+ for (final PackageSetting pkg : mPackages.values()) {
+ ApplicationInfo ai = pkg.pkg.applicationInfo;
+ String dataPath = ai.dataDir;
+ boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+
+ // Avoid any application that has a space in its path
+ // or that is handled by the system.
+ if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID)
+ continue;
+
+ // we store on each line the following information for now:
+ //
+ // pkgName - package name
+ // userId - application-specific user id
+ // debugFlag - 0 or 1 if the package is debuggable.
+ // dataPath - path to package's data path
+ //
+ // NOTE: We prefer not to expose all ApplicationInfo flags for now.
+ //
+ // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
+ // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
+ // system/core/run-as/run-as.c
+ //
+ sb.setLength(0);
+ sb.append(ai.packageName);
+ sb.append(" ");
+ sb.append((int)ai.uid);
+ sb.append(isDebug ? " 1 " : " 0 ");
+ sb.append(dataPath);
+ sb.append("\n");
+ str.write(sb.toString().getBytes());
+ }
+ str.flush();
+ FileUtils.sync(fstr);
+ str.close();
+ journal.commit();
+ }
+ catch (Exception e) {
+ journal.rollback();
+ }
+
+ FileUtils.setPermissions(mPackageListFilename.toString(),
+ FileUtils.S_IRUSR|FileUtils.S_IWUSR
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP
+ |FileUtils.S_IROTH,
+ -1, -1);
+
+ writeStoppedLPr();
+
+ return;
+
+ } catch(XmlPullParserException e) {
+ Log.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
+ + "current changes will be lost at reboot", e);
+ } catch(java.io.IOException e) {
+ Log.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
+ + "current changes will be lost at reboot", e);
+ }
+ // Clean up partially written files
+ if (mSettingsFilename.exists()) {
+ if (!mSettingsFilename.delete()) {
+ Log.wtf(PackageManagerService.TAG, "Failed to clean up mangled file: " + mSettingsFilename);
+ }
+ }
+ //Debug.stopMethodTracing();
+ }
+
+ void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg)
+ throws java.io.IOException {
+ serializer.startTag(null, "updated-package");
+ serializer.attribute(null, "name", pkg.name);
+ if (pkg.realName != null) {
+ serializer.attribute(null, "realName", pkg.realName);
+ }
+ serializer.attribute(null, "codePath", pkg.codePathString);
+ serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
+ serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
+ serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
+ serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
+ 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));
+ } else {
+ serializer.attribute(null, "sharedUserId", Integer.toString(pkg.userId));
+ }
+ serializer.startTag(null, "perms");
+ if (pkg.sharedUser == null) {
+ // If this is a shared user, the permissions will
+ // be written there. We still need to write an
+ // empty permissions list so permissionsFixed will
+ // be set.
+ for (final String name : pkg.grantedPermissions) {
+ BasePermission bp = mPermissions.get(name);
+ if (bp != null) {
+ // We only need to write signature or system permissions but
+ // this wont
+ // match the semantics of grantedPermissions. So write all
+ // permissions.
+ serializer.startTag(null, "item");
+ serializer.attribute(null, "name", name);
+ serializer.endTag(null, "item");
+ }
+ }
+ }
+ serializer.endTag(null, "perms");
+ serializer.endTag(null, "updated-package");
+ }
+
+ void writePackageLPr(XmlSerializer serializer, final PackageSetting pkg)
+ throws java.io.IOException {
+ serializer.startTag(null, "package");
+ serializer.attribute(null, "name", pkg.name);
+ if (pkg.realName != null) {
+ serializer.attribute(null, "realName", pkg.realName);
+ }
+ serializer.attribute(null, "codePath", pkg.codePathString);
+ 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, "ft", Long.toHexString(pkg.timeStamp));
+ serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
+ serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
+ serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
+ if (pkg.sharedUser == null) {
+ serializer.attribute(null, "userId", Integer.toString(pkg.userId));
+ } else {
+ serializer.attribute(null, "sharedUserId", Integer.toString(pkg.userId));
+ }
+ if (pkg.uidError) {
+ serializer.attribute(null, "uidError", "true");
+ }
+ if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
+ serializer.attribute(null, "enabled",
+ pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED ? "true" : "false");
+ }
+ if (pkg.installStatus == PackageSettingBase.PKG_INSTALL_INCOMPLETE) {
+ serializer.attribute(null, "installStatus", "false");
+ }
+ if (pkg.installerPackageName != null) {
+ serializer.attribute(null, "installer", pkg.installerPackageName);
+ }
+ pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
+ if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ serializer.startTag(null, "perms");
+ if (pkg.sharedUser == null) {
+ // If this is a shared user, the permissions will
+ // be written there. We still need to write an
+ // empty permissions list so permissionsFixed will
+ // be set.
+ for (final String name : pkg.grantedPermissions) {
+ serializer.startTag(null, "item");
+ serializer.attribute(null, "name", name);
+ serializer.endTag(null, "item");
+ }
+ }
+ serializer.endTag(null, "perms");
+ }
+ if (pkg.disabledComponents.size() > 0) {
+ serializer.startTag(null, "disabled-components");
+ for (final String name : pkg.disabledComponents) {
+ serializer.startTag(null, "item");
+ serializer.attribute(null, "name", name);
+ serializer.endTag(null, "item");
+ }
+ serializer.endTag(null, "disabled-components");
+ }
+ if (pkg.enabledComponents.size() > 0) {
+ serializer.startTag(null, "enabled-components");
+ for (final String name : pkg.enabledComponents) {
+ serializer.startTag(null, "item");
+ serializer.attribute(null, "name", name);
+ serializer.endTag(null, "item");
+ }
+ serializer.endTag(null, "enabled-components");
+ }
+
+ serializer.endTag(null, "package");
+ }
+
+ void writePermissionLPr(XmlSerializer serializer, BasePermission bp)
+ throws XmlPullParserException, java.io.IOException {
+ if (bp.type != BasePermission.TYPE_BUILTIN && bp.sourcePackage != null) {
+ serializer.startTag(null, "item");
+ serializer.attribute(null, "name", bp.name);
+ serializer.attribute(null, "package", bp.sourcePackage);
+ if (bp.protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
+ serializer.attribute(null, "protection", Integer.toString(bp.protectionLevel));
+ }
+ if (PackageManagerService.DEBUG_SETTINGS)
+ Log.v(PackageManagerService.TAG, "Writing perm: name=" + bp.name + " type="
+ + bp.type);
+ if (bp.type == BasePermission.TYPE_DYNAMIC) {
+ final PermissionInfo pi = bp.perm != null ? bp.perm.info : bp.pendingInfo;
+ if (pi != null) {
+ serializer.attribute(null, "type", "dynamic");
+ if (pi.icon != 0) {
+ serializer.attribute(null, "icon", Integer.toString(pi.icon));
+ }
+ if (pi.nonLocalizedLabel != null) {
+ serializer.attribute(null, "label", pi.nonLocalizedLabel.toString());
+ }
+ }
+ }
+ serializer.endTag(null, "item");
+ }
+ }
+
+ ArrayList<PackageSetting> getListOfIncompleteInstallPackagesLPr() {
+ final HashSet<String> kList = new HashSet<String>(mPackages.keySet());
+ final Iterator<String> its = kList.iterator();
+ final ArrayList<PackageSetting> ret = new ArrayList<PackageSetting>();
+ while (its.hasNext()) {
+ final String key = its.next();
+ final PackageSetting ps = mPackages.get(key);
+ if (ps.getInstallStatus() == PackageSettingBase.PKG_INSTALL_INCOMPLETE) {
+ ret.add(ps);
+ }
+ }
+ return ret;
+ }
+
+ boolean readLPw() {
+ FileInputStream str = null;
+ if (mBackupSettingsFilename.exists()) {
+ try {
+ str = new FileInputStream(mBackupSettingsFilename);
+ mReadMessages.append("Reading from backup settings file\n");
+ PackageManagerService.reportSettingsProblem(Log.INFO,
+ "Need to read from backup settings file");
+ if (mSettingsFilename.exists()) {
+ // If both the backup and settings file exist, we
+ // ignore the settings since it might have been
+ // corrupted.
+ Slog.w(PackageManagerService.TAG, "Cleaning up settings file "
+ + mSettingsFilename);
+ mSettingsFilename.delete();
+ }
+ } catch (java.io.IOException e) {
+ // We'll try for the normal settings file.
+ }
+ }
+
+ mPendingPackages.clear();
+ mPastSignatures.clear();
+
+ try {
+ if (str == null) {
+ if (!mSettingsFilename.exists()) {
+ mReadMessages.append("No settings file found\n");
+ PackageManagerService.reportSettingsProblem(Log.INFO,
+ "No settings file; creating initial state");
+ return false;
+ }
+ str = new FileInputStream(mSettingsFilename);
+ }
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(str, null);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ mReadMessages.append("No start tag found in settings file\n");
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "No start tag found in package manager settings");
+ Log
+ .wtf(PackageManagerService.TAG,
+ "No start tag found in package manager settings");
+ return false;
+ }
+
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("package")) {
+ readPackageLPw(parser);
+ } else if (tagName.equals("permissions")) {
+ readPermissionsLPw(mPermissions, parser);
+ } else if (tagName.equals("permission-trees")) {
+ readPermissionsLPw(mPermissionTrees, parser);
+ } else if (tagName.equals("shared-user")) {
+ readSharedUserLPw(parser);
+ } else if (tagName.equals("preferred-packages")) {
+ // no longer used.
+ } else if (tagName.equals("preferred-activities")) {
+ readPreferredActivitiesLPw(parser);
+ } else if (tagName.equals("updated-package")) {
+ readDisabledSysPackageLPw(parser);
+ } else if (tagName.equals("cleaning-package")) {
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ mPackagesToBeCleaned.add(name);
+ }
+ } else if (tagName.equals("renamed-package")) {
+ String nname = parser.getAttributeValue(null, "new");
+ String oname = parser.getAttributeValue(null, "old");
+ if (nname != null && oname != null) {
+ mRenamedPackages.put(nname, oname);
+ }
+ } else if (tagName.equals("last-platform-version")) {
+ mInternalSdkPlatform = mExternalSdkPlatform = 0;
+ try {
+ String internal = parser.getAttributeValue(null, "internal");
+ if (internal != null) {
+ mInternalSdkPlatform = Integer.parseInt(internal);
+ }
+ String external = parser.getAttributeValue(null, "external");
+ if (external != null) {
+ mExternalSdkPlatform = Integer.parseInt(external);
+ }
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ str.close();
+
+ } catch (XmlPullParserException e) {
+ mReadMessages.append("Error reading: " + e.toString());
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
+ Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+
+ } catch (java.io.IOException e) {
+ mReadMessages.append("Error reading: " + e.toString());
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
+ Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+
+ }
+
+ final int N = mPendingPackages.size();
+ for (int i = 0; i < N; i++) {
+ final PendingPackage pp = mPendingPackages.get(i);
+ Object idObj = getUserIdLPr(pp.sharedId);
+ if (idObj != null && idObj instanceof SharedUserSetting) {
+ PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
+ (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
+ pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
+ if (p == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unable to create application package for " + pp.name);
+ continue;
+ }
+ p.copyFrom(pp);
+ } else if (idObj != null) {
+ String msg = "Bad package setting: package " + pp.name + " has shared uid "
+ + pp.sharedId + " that is not a shared uid\n";
+ mReadMessages.append(msg);
+ PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
+ } else {
+ String msg = "Bad package setting: package " + pp.name + " has shared uid "
+ + pp.sharedId + " that is not defined\n";
+ mReadMessages.append(msg);
+ PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
+ }
+ }
+ mPendingPackages.clear();
+
+ /*
+ * Make sure all the updated system packages have their shared users
+ * associated with them.
+ */
+ final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
+ while (disabledIt.hasNext()) {
+ final PackageSetting disabledPs = disabledIt.next();
+ final Object id = getUserIdLPr(disabledPs.userId);
+ if (id != null && id instanceof SharedUserSetting) {
+ disabledPs.sharedUser = (SharedUserSetting) id;
+ }
+ }
+
+ readStoppedLPw();
+
+ mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, "
+ + mSharedUsers.size() + " shared uids\n");
+
+ return true;
+ }
+
+ private int readInt(XmlPullParser parser, String ns, String name, int defValue) {
+ String v = parser.getAttributeValue(ns, name);
+ try {
+ if (v == null) {
+ return defValue;
+ }
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: attribute " + name
+ + " has bad integer value " + v + " at "
+ + parser.getPositionDescription());
+ }
+ return defValue;
+ }
+
+ private void readPermissionsLPw(HashMap<String, BasePermission> out, XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ final String tagName = parser.getName();
+ if (tagName.equals("item")) {
+ final String name = parser.getAttributeValue(null, "name");
+ final String sourcePackage = parser.getAttributeValue(null, "package");
+ final String ptype = parser.getAttributeValue(null, "type");
+ if (name != null && sourcePackage != null) {
+ final boolean dynamic = "dynamic".equals(ptype);
+ final BasePermission bp = new BasePermission(name, sourcePackage,
+ dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
+ bp.protectionLevel = readInt(parser, null, "protection",
+ PermissionInfo.PROTECTION_NORMAL);
+ if (dynamic) {
+ PermissionInfo pi = new PermissionInfo();
+ pi.packageName = sourcePackage.intern();
+ pi.name = name.intern();
+ pi.icon = readInt(parser, null, "icon", 0);
+ pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
+ pi.protectionLevel = bp.protectionLevel;
+ bp.pendingInfo = pi;
+ }
+ out.put(bp.name, bp);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: permissions has" + " no name at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element reading permissions: " + parser.getName() + " at "
+ + parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ String name = parser.getAttributeValue(null, "name");
+ String realName = parser.getAttributeValue(null, "realName");
+ String codePathStr = parser.getAttributeValue(null, "codePath");
+ String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ if (resourcePathStr == null) {
+ resourcePathStr = codePathStr;
+ }
+ String version = parser.getAttributeValue(null, "version");
+ int versionCode = 0;
+ if (version != null) {
+ try {
+ versionCode = Integer.parseInt(version);
+ } catch (NumberFormatException e) {
+ }
+ }
+
+ int pkgFlags = 0;
+ pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
+ new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags);
+ String timeStampStr = parser.getAttributeValue(null, "ft");
+ if (timeStampStr != null) {
+ try {
+ long timeStamp = Long.parseLong(timeStampStr, 16);
+ ps.setTimeStamp(timeStamp);
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ timeStampStr = parser.getAttributeValue(null, "ts");
+ if (timeStampStr != null) {
+ try {
+ long timeStamp = Long.parseLong(timeStampStr);
+ ps.setTimeStamp(timeStamp);
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "it");
+ if (timeStampStr != null) {
+ try {
+ ps.firstInstallTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "ut");
+ if (timeStampStr != null) {
+ try {
+ ps.lastUpdateTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ String idStr = parser.getAttributeValue(null, "userId");
+ ps.userId = idStr != null ? Integer.parseInt(idStr) : 0;
+ if (ps.userId <= 0) {
+ String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
+ ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
+ }
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("perms")) {
+ readGrantedPermissionsLPw(parser, ps.grantedPermissions);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <updated-package>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ mDisabledSysPackages.put(name, ps);
+ }
+
+ private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String name = null;
+ String realName = null;
+ String idStr = null;
+ String sharedIdStr = null;
+ String codePathStr = null;
+ String resourcePathStr = null;
+ String nativeLibraryPathStr = null;
+ String systemStr = null;
+ String installerPackageName = null;
+ String uidError = null;
+ int pkgFlags = 0;
+ long timeStamp = 0;
+ long firstInstallTime = 0;
+ long lastUpdateTime = 0;
+ PackageSettingBase packageSetting = null;
+ String version = null;
+ int versionCode = 0;
+ try {
+ name = parser.getAttributeValue(null, "name");
+ realName = parser.getAttributeValue(null, "realName");
+ idStr = parser.getAttributeValue(null, "userId");
+ uidError = parser.getAttributeValue(null, "uidError");
+ sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
+ codePathStr = parser.getAttributeValue(null, "codePath");
+ resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ version = parser.getAttributeValue(null, "version");
+ if (version != null) {
+ try {
+ versionCode = Integer.parseInt(version);
+ } catch (NumberFormatException e) {
+ }
+ }
+ installerPackageName = parser.getAttributeValue(null, "installer");
+
+ systemStr = parser.getAttributeValue(null, "flags");
+ if (systemStr != null) {
+ try {
+ pkgFlags = Integer.parseInt(systemStr);
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ // For backward compatibility
+ systemStr = parser.getAttributeValue(null, "system");
+ if (systemStr != null) {
+ pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM
+ : 0;
+ } else {
+ // Old settings that don't specify system... just treat
+ // them as system, good enough.
+ pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ }
+ String timeStampStr = parser.getAttributeValue(null, "ft");
+ if (timeStampStr != null) {
+ try {
+ timeStamp = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ timeStampStr = parser.getAttributeValue(null, "ts");
+ if (timeStampStr != null) {
+ try {
+ timeStamp = Long.parseLong(timeStampStr);
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "it");
+ if (timeStampStr != null) {
+ try {
+ firstInstallTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "ut");
+ if (timeStampStr != null) {
+ try {
+ lastUpdateTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ if (PackageManagerService.DEBUG_SETTINGS)
+ Log.v(PackageManagerService.TAG, "Reading package: " + name + " userId=" + idStr
+ + " sharedUserId=" + sharedIdStr);
+ int userId = idStr != null ? Integer.parseInt(idStr) : 0;
+ if (resourcePathStr == null) {
+ resourcePathStr = codePathStr;
+ }
+ if (realName != null) {
+ realName = realName.intern();
+ }
+ if (name == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <package> has no name at "
+ + parser.getPositionDescription());
+ } else if (codePathStr == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <package> has no codePath at "
+ + parser.getPositionDescription());
+ } else if (userId > 0) {
+ packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
+ new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode,
+ pkgFlags);
+ if (PackageManagerService.DEBUG_SETTINGS)
+ Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ + userId + " pkg=" + packageSetting);
+ if (packageSetting == null) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding uid "
+ + userId + " while parsing settings at "
+ + parser.getPositionDescription());
+ } else {
+ packageSetting.setTimeStamp(timeStamp);
+ packageSetting.firstInstallTime = firstInstallTime;
+ packageSetting.lastUpdateTime = lastUpdateTime;
+ }
+ } else if (sharedIdStr != null) {
+ userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
+ if (userId > 0) {
+ packageSetting = new PendingPackage(name.intern(), realName, new File(
+ codePathStr), new File(resourcePathStr), nativeLibraryPathStr, userId,
+ versionCode, pkgFlags);
+ packageSetting.setTimeStamp(timeStamp);
+ packageSetting.firstInstallTime = firstInstallTime;
+ packageSetting.lastUpdateTime = lastUpdateTime;
+ mPendingPackages.add((PendingPackage) packageSetting);
+ if (PackageManagerService.DEBUG_SETTINGS)
+ Log.i(PackageManagerService.TAG, "Reading package " + name
+ + ": sharedUserId=" + userId + " pkg=" + packageSetting);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: package " + name
+ + " has bad sharedId " + sharedIdStr + " at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: package " + name + " has bad userId "
+ + idStr + " at " + parser.getPositionDescription());
+ }
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: package " + name + " has bad userId "
+ + idStr + " at " + parser.getPositionDescription());
+ }
+ if (packageSetting != null) {
+ packageSetting.uidError = "true".equals(uidError);
+ packageSetting.installerPackageName = installerPackageName;
+ packageSetting.nativeLibraryPathString = nativeLibraryPathStr;
+ final String enabledStr = parser.getAttributeValue(null, "enabled");
+ if (enabledStr != null) {
+ if (enabledStr.equalsIgnoreCase("true")) {
+ packageSetting.enabled = COMPONENT_ENABLED_STATE_ENABLED;
+ } else if (enabledStr.equalsIgnoreCase("false")) {
+ packageSetting.enabled = COMPONENT_ENABLED_STATE_DISABLED;
+ } else if (enabledStr.equalsIgnoreCase("default")) {
+ packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: package " + name
+ + " has bad enabled value: " + idStr + " at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+ final String installStatusStr = parser.getAttributeValue(null, "installStatus");
+ if (installStatusStr != null) {
+ if (installStatusStr.equalsIgnoreCase("false")) {
+ packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_INCOMPLETE;
+ } else {
+ packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;
+ }
+ }
+
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("disabled-components")) {
+ readDisabledComponentsLPw(packageSetting, parser);
+ } else if (tagName.equals("enabled-components")) {
+ readEnabledComponentsLPw(packageSetting, parser);
+ } else if (tagName.equals("sigs")) {
+ packageSetting.signatures.readXml(parser, mPastSignatures);
+ } else if (tagName.equals("perms")) {
+ readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
+ packageSetting.permissionsFixed = true;
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <package>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readDisabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("item")) {
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ packageSetting.disabledComponents.add(name.intern());
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <disabled-components> has"
+ + " no name at " + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <disabled-components>: " + parser.getName());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readEnabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("item")) {
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ packageSetting.enabledComponents.add(name.intern());
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <enabled-components> has"
+ + " no name at " + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <enabled-components>: " + parser.getName());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readSharedUserLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String name = null;
+ String idStr = null;
+ int pkgFlags = 0;
+ SharedUserSetting su = null;
+ try {
+ name = parser.getAttributeValue(null, "name");
+ idStr = parser.getAttributeValue(null, "userId");
+ int userId = idStr != null ? Integer.parseInt(idStr) : 0;
+ if ("true".equals(parser.getAttributeValue(null, "system"))) {
+ pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ if (name == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <shared-user> has no name at "
+ + parser.getPositionDescription());
+ } else if (userId == 0) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: shared-user " + name
+ + " has bad userId " + idStr + " at "
+ + parser.getPositionDescription());
+ } else {
+ if ((su = addSharedUserLPw(name.intern(), userId, pkgFlags)) == null) {
+ PackageManagerService
+ .reportSettingsProblem(Log.ERROR, "Occurred while parsing settings at "
+ + parser.getPositionDescription());
+ }
+ }
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: package " + name + " has bad userId "
+ + idStr + " at " + parser.getPositionDescription());
+ }
+ ;
+
+ if (su != null) {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("sigs")) {
+ su.signatures.readXml(parser, mPastSignatures);
+ } else if (tagName.equals("perms")) {
+ readGrantedPermissionsLPw(parser, su.grantedPermissions);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <shared-user>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readGrantedPermissionsLPw(XmlPullParser parser, HashSet<String> outPerms)
+ throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("item")) {
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ outPerms.add(name.intern());
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <perms> has" + " no name at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <perms>: " + parser.getName());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readPreferredActivitiesLPw(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("item")) {
+ PreferredActivity pa = new PreferredActivity(parser);
+ if (pa.mPref.getParseError() == null) {
+ mPreferredActivities.addFilter(pa);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <preferred-activity> "
+ + pa.mPref.getParseError() + " at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <preferred-activities>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ // Returns -1 if we could not find an available UserId to assign
+ private int newUserIdLPw(Object obj) {
+ // Let's be stupidly inefficient for now...
+ final int N = mUserIds.size();
+ for (int i = 0; i < N; i++) {
+ if (mUserIds.get(i) == null) {
+ mUserIds.set(i, obj);
+ return PackageManagerService.FIRST_APPLICATION_UID + i;
+ }
+ }
+
+ // None left?
+ if (N >= PackageManagerService.MAX_APPLICATION_UIDS) {
+ return -1;
+ }
+
+ mUserIds.add(obj);
+ return PackageManagerService.FIRST_APPLICATION_UID + N;
+ }
+
+ public PackageSetting getDisabledSystemPkgLPr(String name) {
+ PackageSetting ps = mDisabledSysPackages.get(name);
+ return ps;
+ }
+
+ boolean isEnabledLPr(ComponentInfo componentInfo, int flags) {
+ if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
+ return true;
+ }
+ final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
+ if (PackageManagerService.DEBUG_SETTINGS) {
+ Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = " + componentInfo.packageName
+ + " componentName = " + componentInfo.name);
+ Log.v(PackageManagerService.TAG, "enabledComponents: "
+ + Arrays.toString(packageSettings.enabledComponents.toArray()));
+ Log.v(PackageManagerService.TAG, "disabledComponents: "
+ + Arrays.toString(packageSettings.disabledComponents.toArray()));
+ }
+ if (packageSettings == null) {
+ 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 componentInfo.enabled;
+ }
+
+ String getInstallerPackageNameLPr(String packageName) {
+ final PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return pkg.installerPackageName;
+ }
+
+ int getApplicationEnabledSettingLPr(String packageName) {
+ final PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return pkg.enabled;
+ }
+
+ int getComponentEnabledSettingLPr(ComponentName componentName) {
+ final String packageName = componentName.getPackageName();
+ final PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown component: " + componentName);
+ }
+ final String classNameStr = componentName.getClassName();
+ return pkg.getCurrentEnabledStateLPr(classNameStr);
+ }
+
+ boolean setPackageStoppedStateLPw(String packageName, boolean stopped,
+ boolean allowedByPermission, int uid) {
+ final PackageSetting pkgSetting = mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (!allowedByPermission && (uid != pkgSetting.userId)) {
+ throw new SecurityException(
+ "Permission Denial: attempt to change stopped state from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + uid + ", package uid=" + pkgSetting.userId);
+ }
+ if (DEBUG_STOPPED) {
+ if (stopped) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Slog.i(TAG, "Stopping package " + packageName, e);
+ }
+ }
+ if (pkgSetting.stopped != stopped) {
+ pkgSetting.stopped = stopped;
+ pkgSetting.pkg.mSetStopped = stopped;
+ if (pkgSetting.notLaunched) {
+ if (pkgSetting.installerPackageName != null) {
+ PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
+ pkgSetting.name, null,
+ pkgSetting.installerPackageName, null);
+ }
+ pkgSetting.notLaunched = false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ final Date date = new Date();
+ boolean printedSomething = false;
+ for (final PackageSetting ps : mPackages.values()) {
+ if (packageName != null && !packageName.equals(ps.realName)
+ && !packageName.equals(ps.name)) {
+ continue;
+ }
+
+ if (packageName != null) {
+ dumpState.setSharedUser(ps.sharedUser);
+ }
+
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Packages:");
+ printedSomething = true;
+ }
+ pw.print(" Package [");
+ pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(ps)));
+ pw.println("):");
+
+ if (ps.realName != null) {
+ pw.print(" compat name=");
+ pw.println(ps.name);
+ }
+
+ pw.print(" userId="); pw.print(ps.userId);
+ pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
+ pw.print(" sharedUser="); pw.println(ps.sharedUser);
+ 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(" versionCode="); pw.println(ps.versionCode);
+ if (ps.pkg != null) {
+ pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
+ 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;
+ pw.print("medium");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("large");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("xlarge");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("resizeable");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("anyDensity");
+ }
+ }
+ pw.println("]");
+ pw.print(" timeStamp=");
+ date.setTime(ps.timeStamp);
+ pw.println(sdf.format(date));
+ pw.print(" firstInstallTime=");
+ date.setTime(ps.firstInstallTime);
+ pw.println(sdf.format(date));
+ pw.print(" lastUpdateTime=");
+ date.setTime(ps.lastUpdateTime);
+ pw.println(sdf.format(date));
+ if (ps.installerPackageName != null) {
+ pw.print(" installerPackageName="); pw.println(ps.installerPackageName);
+ }
+ pw.print(" signatures="); pw.println(ps.signatures);
+ pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
+ pw.print(" haveGids="); pw.println(ps.haveGids);
+ pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
+ pw.print(" installStatus="); pw.print(ps.installStatus);
+ pw.print(" stopped="); pw.print(ps.stopped);
+ pw.print(" enabled="); pw.println(ps.enabled);
+ if (ps.disabledComponents.size() > 0) {
+ pw.println(" disabledComponents:");
+ for (String s : ps.disabledComponents) {
+ pw.print(" "); pw.println(s);
+ }
+ }
+ if (ps.enabledComponents.size() > 0) {
+ pw.println(" enabledComponents:");
+ for (String s : ps.enabledComponents) {
+ pw.print(" "); pw.println(s);
+ }
+ }
+ if (ps.grantedPermissions.size() > 0) {
+ pw.println(" grantedPermissions:");
+ for (String s : ps.grantedPermissions) {
+ pw.print(" "); pw.println(s);
+ }
+ }
+ }
+
+ printedSomething = false;
+ if (mRenamedPackages.size() > 0) {
+ for (final HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) {
+ if (packageName != null && !packageName.equals(e.getKey())
+ && !packageName.equals(e.getValue())) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Renamed packages:");
+ printedSomething = true;
+ }
+ pw.print(" ");
+ pw.print(e.getKey());
+ pw.print(" -> ");
+ pw.println(e.getValue());
+ }
+ }
+
+ printedSomething = false;
+ if (mDisabledSysPackages.size() > 0) {
+ for (final PackageSetting ps : mDisabledSysPackages.values()) {
+ if (packageName != null && !packageName.equals(ps.realName)
+ && !packageName.equals(ps.name)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Hidden system packages:");
+ printedSomething = true;
+ }
+ pw.print(" Package [");
+ pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(ps)));
+ pw.println("):");
+ if (ps.realName != null) {
+ pw.print(" compat name=");
+ pw.println(ps.name);
+ }
+ pw.print(" userId=");
+ pw.println(ps.userId);
+ pw.print(" sharedUser=");
+ pw.println(ps.sharedUser);
+ pw.print(" codePath=");
+ pw.println(ps.codePathString);
+ pw.print(" resourcePath=");
+ pw.println(ps.resourcePathString);
+ }
+ }
+ }
+
+ void dumpPermissionsLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+ boolean printedSomething = false;
+ for (BasePermission p : mPermissions.values()) {
+ if (packageName != null && !packageName.equals(p.sourcePackage)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Permissions:");
+ printedSomething = true;
+ }
+ pw.print(" Permission ["); pw.print(p.name); pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(p)));
+ pw.println("):");
+ pw.print(" sourcePackage="); pw.println(p.sourcePackage);
+ pw.print(" uid="); pw.print(p.uid);
+ pw.print(" gids="); pw.print(PackageManagerService.arrayToString(p.gids));
+ pw.print(" type="); pw.print(p.type);
+ pw.print(" prot="); pw.println(p.protectionLevel);
+ if (p.packageSetting != null) {
+ pw.print(" packageSetting="); pw.println(p.packageSetting);
+ }
+ if (p.perm != null) {
+ pw.print(" perm="); pw.println(p.perm);
+ }
+ }
+ }
+
+ void dumpSharedUsersLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+ boolean printedSomething = false;
+ for (SharedUserSetting su : mSharedUsers.values()) {
+ if (packageName != null && su != dumpState.getSharedUser()) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Shared users:");
+ printedSomething = true;
+ }
+ pw.print(" SharedUser [");
+ pw.print(su.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(su)));
+ pw.println("):");
+ pw.print(" userId=");
+ pw.print(su.userId);
+ pw.print(" gids=");
+ pw.println(PackageManagerService.arrayToString(su.gids));
+ pw.println(" grantedPermissions:");
+ for (String s : su.grantedPermissions) {
+ pw.print(" ");
+ pw.println(s);
+ }
+ }
+ }
+
+ void dumpReadMessagesLPr(PrintWriter pw, DumpState dumpState) {
+ pw.println("Settings parse messages:");
+ pw.print(mReadMessages.toString());
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/SharedUserSetting.java b/services/java/com/android/server/pm/SharedUserSetting.java
new file mode 100644
index 0000000..76826ea
--- /dev/null
+++ b/services/java/com/android/server/pm/SharedUserSetting.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import java.util.HashSet;
+
+/**
+ * Settings data for a particular shared user ID we know about.
+ */
+final class SharedUserSetting extends GrantedPermissions {
+ final String name;
+
+ int userId;
+
+ final HashSet<PackageSetting> packages = new HashSet<PackageSetting>();
+
+ final PackageSignatures signatures = new PackageSignatures();
+
+ SharedUserSetting(String _name, int _pkgFlags) {
+ super(_pkgFlags);
+ name = _name;
+ }
+
+ @Override
+ public String toString() {
+ return "SharedUserSetting{" + Integer.toHexString(System.identityHashCode(this)) + " "
+ + name + "/" + userId + "}";
+ }
+}
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
new file mode 100644
index 0000000..76fa5ab
--- /dev/null
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+public class UserManager {
+ private static final String TAG_NAME = "name";
+
+ private static final String ATTR_FLAGS = "flags";
+
+ private static final String ATTR_ID = "id";
+
+ private static final String TAG_USERS = "users";
+
+ private static final String TAG_USER = "user";
+
+ private static final String LOG_TAG = "UserManager";
+
+ private static final String USER_INFO_DIR = "system" + File.separator + "users";
+ private static final String USER_LIST_FILENAME = "userlist.xml";
+
+ private SparseArray<UserInfo> mUsers;
+
+ private final File mUsersDir;
+ private final File mUserListFile;
+ private int[] mUserIds;
+
+ private Installer mInstaller;
+ private File mBaseUserPath;
+
+ /**
+ * Available for testing purposes.
+ */
+ UserManager(File dataDir, File baseUserPath) {
+ mUsersDir = new File(dataDir, USER_INFO_DIR);
+ mUsersDir.mkdirs();
+ mBaseUserPath = baseUserPath;
+ FileUtils.setPermissions(mUsersDir.toString(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG
+ |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+ -1, -1);
+ mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+ readUserList();
+ }
+
+ public UserManager(Installer installer, File baseUserPath) {
+ this(Environment.getDataDirectory(), baseUserPath);
+ mInstaller = installer;
+ }
+
+ public List<UserInfo> getUsers() {
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ for (int i = 0; i < mUsers.size(); i++) {
+ users.add(mUsers.valueAt(i));
+ }
+ return users;
+ }
+
+ /**
+ * Returns an array of user ids. This array is cached here for quick access, so do not modify or
+ * cache it elsewhere.
+ * @return the array of user ids.
+ */
+ int[] getUserIds() {
+ return mUserIds;
+ }
+
+ private void readUserList() {
+ mUsers = new SparseArray<UserInfo>();
+ if (!mUserListFile.exists()) {
+ fallbackToSingleUser();
+ return;
+ }
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(mUserListFile);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read user list");
+ fallbackToSingleUser();
+ return;
+ }
+
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ UserInfo user = readUser(Integer.parseInt(id));
+ if (user != null) {
+ mUsers.put(user.id, user);
+ }
+ }
+ }
+ updateUserIds();
+ } catch (IOException ioe) {
+ fallbackToSingleUser();
+ } catch (XmlPullParserException pe) {
+ fallbackToSingleUser();
+ }
+ }
+
+ private void fallbackToSingleUser() {
+ // Create the primary user
+ UserInfo primary = new UserInfo(0, "Primary",
+ UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+ mUsers.put(0, primary);
+ updateUserIds();
+
+ writeUserList();
+ writeUser(primary);
+ }
+
+ /*
+ * Writes the user file in this format:
+ *
+ * <user flags="20039023" id="0">
+ * <name>Primary</name>
+ * </user>
+ */
+ private void writeUser(UserInfo userInfo) {
+ try {
+ final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
+ final FileOutputStream fos = new FileOutputStream(mUserFile);
+ final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ // XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(bos, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_USER);
+ serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
+ serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
+
+ serializer.startTag(null, TAG_NAME);
+ serializer.text(userInfo.name);
+ serializer.endTag(null, TAG_NAME);
+
+ serializer.endTag(null, TAG_USER);
+
+ serializer.endDocument();
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+ }
+ }
+
+ /*
+ * Writes the user list file in this format:
+ *
+ * <users>
+ * <user id="0"></user>
+ * <user id="2"></user>
+ * </users>
+ */
+ private void writeUserList() {
+ try {
+ final FileOutputStream fos = new FileOutputStream(mUserListFile);
+ final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ // XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(bos, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_USERS);
+
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ serializer.startTag(null, TAG_USER);
+ serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
+ serializer.endTag(null, TAG_USER);
+ }
+
+ serializer.endTag(null, TAG_USERS);
+
+ serializer.endDocument();
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Error writing user list");
+ }
+ }
+
+ private UserInfo readUser(int id) {
+ int flags = 0;
+ String name = null;
+
+ FileInputStream fis = null;
+ try {
+ File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
+ fis = new FileInputStream(userFile);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read user " + id);
+ return null;
+ }
+
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
+ String storedId = parser.getAttributeValue(null, ATTR_ID);
+ if (Integer.parseInt(storedId) != id) {
+ Slog.e(LOG_TAG, "User id does not match the file name");
+ return null;
+ }
+ String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
+ flags = Integer.parseInt(flagString);
+
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ name = parser.getText();
+ }
+ }
+ }
+ fis.close();
+
+ UserInfo userInfo = new UserInfo(id, name, flags);
+ return userInfo;
+
+ } catch (IOException ioe) {
+ } catch (XmlPullParserException pe) {
+ }
+ return null;
+ }
+
+ public UserInfo createUser(String name, int flags, List<ApplicationInfo> apps) {
+ int userId = getNextAvailableId();
+ UserInfo userInfo = new UserInfo(userId, name, flags);
+ File userPath = new File(mBaseUserPath, Integer.toString(userId));
+ if (!createPackageFolders(userId, userPath, apps)) {
+ return null;
+ }
+ mUsers.put(userId, userInfo);
+ writeUserList();
+ writeUser(userInfo);
+ updateUserIds();
+ return userInfo;
+ }
+
+ /**
+ * Removes a user and all data directories created for that user. This method should be called
+ * after the user's processes have been terminated.
+ * @param id the user's id
+ */
+ public void removeUser(int id) {
+ // Remove from the list
+ UserInfo userInfo = mUsers.get(id);
+ if (userInfo != null) {
+ // Remove this user from the list
+ mUsers.remove(id);
+ // Remove user file
+ File userFile = new File(mUsersDir, id + ".xml");
+ userFile.delete();
+ // Update the user list
+ writeUserList();
+ // Remove the data directories for all packages for this user
+ removePackageFolders(id);
+ updateUserIds();
+ }
+ }
+
+ public void installPackageForAllUsers(String packageName, int uid) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+ userId);
+ }
+ }
+
+ public void clearUserDataForAllUsers(String packageName) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.clearUserData(packageName, userId);
+ }
+ }
+
+ public void removePackageForAllUsers(String packageName) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.remove(packageName, userId);
+ }
+ }
+
+ /**
+ * Caches the list of user ids in an array, adjusting the array size when necessary.
+ */
+ private void updateUserIds() {
+ if (mUserIds == null || mUserIds.length != mUsers.size()) {
+ mUserIds = new int[mUsers.size()];
+ }
+ for (int i = 0; i < mUsers.size(); i++) {
+ mUserIds[i] = mUsers.keyAt(i);
+ }
+ }
+
+ /**
+ * Returns the next available user id, filling in any holes in the ids.
+ * @return
+ */
+ private int getNextAvailableId() {
+ int i = 0;
+ while (i < Integer.MAX_VALUE) {
+ if (mUsers.indexOfKey(i) < 0) {
+ break;
+ }
+ i++;
+ }
+ return i;
+ }
+
+ private boolean createPackageFolders(int id, File userPath, final List<ApplicationInfo> apps) {
+ // mInstaller may not be available for unit-tests.
+ if (mInstaller == null || apps == null) return true;
+
+ final long startTime = SystemClock.elapsedRealtime();
+ // Create the user path
+ userPath.mkdir();
+ FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+ | FileUtils.S_IXOTH, -1, -1);
+
+ // Create the individual data directories
+ for (ApplicationInfo app : apps) {
+ if (app.uid > android.os.Process.FIRST_APPLICATION_UID
+ && app.uid < PackageManager.PER_USER_RANGE) {
+ mInstaller.createUserData(app.packageName,
+ PackageManager.getUid(id, app.uid), id);
+ }
+ }
+ final long stopTime = SystemClock.elapsedRealtime();
+ Log.i(LOG_TAG,
+ "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
+ return true;
+ }
+
+ private boolean removePackageFolders(int id) {
+ // mInstaller may not be available for unit-tests.
+ if (mInstaller == null) return true;
+
+ mInstaller.removeUserDataDirs(id);
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/wm/DimSurface.java b/services/java/com/android/server/wm/DimSurface.java
index 084ac6f..220c7fb 100644
--- a/services/java/com/android/server/wm/DimSurface.java
+++ b/services/java/com/android/server/wm/DimSurface.java
@@ -89,7 +89,7 @@ class DimSurface {
public void printTo(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mDimSurface="); pw.println(mDimSurface);
pw.print(prefix); pw.print("mDimShown="); pw.print(mDimShown);
- pw.print(" mLayer="); pw.println(mLayer);
+ pw.print(" mLayer="); pw.print(mLayer);
pw.print(" mDimColor=0x"); pw.println(Integer.toHexString(mDimColor));
pw.print(prefix); pw.print("mLastDimWidth="); pw.print(mLastDimWidth);
pw.print(" mLastDimWidth="); pw.println(mLastDimWidth);
diff --git a/services/java/com/android/server/wm/InputFilter.java b/services/java/com/android/server/wm/InputFilter.java
new file mode 100644
index 0000000..8f0001a
--- /dev/null
+++ b/services/java/com/android/server/wm/InputFilter.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.InputEvent;
+import android.view.InputEventConsistencyVerifier;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+
+/**
+ * Filters input events before they are dispatched to the system.
+ * <p>
+ * At most one input filter can be installed by calling
+ * {@link WindowManagerService#setInputFilter}. When an input filter is installed, the
+ * system's behavior changes as follows:
+ * <ul>
+ * <li>Input events are first delivered to the {@link WindowManagerPolicy}
+ * interception methods before queueing as usual. This critical step takes care of managing
+ * the power state of the device and handling wake keys.</li>
+ * <li>Input events are then asynchronously delivered to the input filter's
+ * {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to
+ * applications as usual. The input filter only receives input events that were
+ * generated by input device; the input filter will not receive input events that were
+ * injected into the system by other means, such as by instrumentation.</li>
+ * <li>The input filter processes and optionally transforms the stream of events. For example,
+ * it may transform a sequence of motion events representing an accessibility gesture into
+ * a different sequence of motion events, key presses or other system-level interactions.
+ * The input filter can send events to be dispatched by calling
+ * {@link #sendInputEvent(InputEvent)} and passing appropriate policy flags for the
+ * input event.</li>
+ * </ul>
+ * </p>
+ * <h3>The importance of input event consistency</h3>
+ * <p>
+ * The input filter mechanism is very low-level. At a minimum, it needs to ensure that it
+ * sends an internally consistent stream of input events to the dispatcher. There are
+ * very important invariants to be maintained.
+ * </p><p>
+ * For example, if a key down is sent, a corresponding key up should also be sent eventually.
+ * Likewise, for touch events, each pointer must individually go down with
+ * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} and then
+ * individually go up with {@link MotionEvent#ACTION_POINTER_UP} or {@link MotionEvent#ACTION_UP}
+ * and the sequence of pointer ids used must be consistent throughout the gesture.
+ * </p><p>
+ * Sometimes a filter may wish to cancel a previously dispatched key or motion. It should
+ * use {@link KeyEvent#FLAG_CANCELED} or {@link MotionEvent#ACTION_CANCEL} accordingly.
+ * </p><p>
+ * The input filter must take into account the fact that the input events coming from different
+ * devices or even different sources all consist of distinct streams of input.
+ * Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify
+ * the source of the event and its semantics. There are be multiple sources of keys,
+ * touches and other input: they must be kept separate.
+ * </p>
+ * <h3>Policy flags</h3>
+ * <p>
+ * Input events received from the dispatcher and sent to the dispatcher have policy flags
+ * associated with them. Policy flags control some functions of the dispatcher.
+ * </p><p>
+ * The early policy interception decides whether an input event should be delivered
+ * to applications or dropped. The policy indicates its decision by setting the
+ * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} policy flag. The input filter may
+ * sometimes receive events that do not have this flag set. It should take note of
+ * the fact that the policy intends to drop the event, clean up its state, and
+ * then send appropriate cancelation events to the dispatcher if needed.
+ * </p><p>
+ * For example, suppose the input filter is processing a gesture and one of the touch events
+ * it receives does not have the {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag set.
+ * The input filter should clear its internal state about the gesture and then send key or
+ * motion events to the dispatcher to cancel any keys or pointers that are down.
+ * </p><p>
+ * Corollary: Events that set sent to the dispatcher should usually include the
+ * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag. Otherwise, they will be dropped!
+ * </p><p>
+ * It may be prudent to disable automatic key repeating for synthetically generated
+ * keys by setting the {@link WindowManagerPolicy#FLAG_DISABLE_KEY_REPEAT} policy flag.
+ * </p>
+ */
+public abstract class InputFilter {
+ private static final int MSG_INSTALL = 1;
+ private static final int MSG_UNINSTALL = 2;
+ private static final int MSG_INPUT_EVENT = 3;
+
+ private final H mH;
+ private Host mHost;
+
+ // Consistency verifiers for debugging purposes.
+ private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this,
+ InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
+ "InputFilter#InboundInputEventConsistencyVerifier") : null;
+ private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this,
+ InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
+ "InputFilter#OutboundInputEventConsistencyVerifier") : null;
+
+ /**
+ * Creates the input filter.
+ *
+ * @param looper The looper to run callbacks on.
+ */
+ public InputFilter(Looper looper) {
+ mH = new H(looper);
+ }
+
+ /**
+ * Called when the input filter is installed.
+ * This method is guaranteed to be non-reentrant.
+ *
+ * @param host The input filter host environment.
+ */
+ final void install(Host host) {
+ mH.obtainMessage(MSG_INSTALL, host).sendToTarget();
+ }
+
+ /**
+ * Called when the input filter is uninstalled.
+ * This method is guaranteed to be non-reentrant.
+ */
+ final void uninstall() {
+ mH.obtainMessage(MSG_UNINSTALL).sendToTarget();
+ }
+
+ /**
+ * Called to enqueue the input event for filtering.
+ * The event will be recycled after the input filter processes it.
+ * This method is guaranteed to be non-reentrant.
+ *
+ * @param event The input event to enqueue.
+ */
+ final void filterInputEvent(InputEvent event, int policyFlags) {
+ mH.obtainMessage(MSG_INPUT_EVENT, policyFlags, 0, event).sendToTarget();
+ }
+
+ /**
+ * Sends an input event to the dispatcher.
+ *
+ * @param event The input event to publish.
+ * @param policyFlags The input event policy flags.
+ */
+ public void sendInputEvent(InputEvent event, int policyFlags) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+ if (mHost == null) {
+ throw new IllegalStateException("Cannot send input event because the input filter " +
+ "is not installed.");
+ }
+ if (mOutboundInputEventConsistencyVerifier != null) {
+ mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0);
+ }
+ mHost.sendInputEvent(event, policyFlags);
+ }
+
+ /**
+ * Called when an input event has been received from the dispatcher.
+ * <p>
+ * The default implementation sends the input event back to the dispatcher, unchanged.
+ * </p><p>
+ * The event will be recycled when this method returns. If you want to keep it around,
+ * make a copy!
+ * </p>
+ *
+ * @param event The input event that was received.
+ * @param policyFlags The input event policy flags.
+ */
+ public void onInputEvent(InputEvent event, int policyFlags) {
+ sendInputEvent(event, policyFlags);
+ }
+
+ /**
+ * Called when the filter is installed into the dispatch pipeline.
+ * <p>
+ * This method is called before the input filter receives any input events.
+ * The input filter should take this opportunity to prepare itself.
+ * </p>
+ */
+ public void onInstalled() {
+ }
+
+ /**
+ * Called when the filter is uninstalled from the dispatch pipeline.
+ * <p>
+ * This method is called after the input filter receives its last input event.
+ * The input filter should take this opportunity to clean up.
+ * </p>
+ */
+ public void onUninstalled() {
+ }
+
+ private final class H extends Handler {
+ public H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INSTALL:
+ mHost = (Host)msg.obj;
+ if (mInboundInputEventConsistencyVerifier != null) {
+ mInboundInputEventConsistencyVerifier.reset();
+ }
+ if (mOutboundInputEventConsistencyVerifier != null) {
+ mOutboundInputEventConsistencyVerifier.reset();
+ }
+ onInstalled();
+ break;
+
+ case MSG_UNINSTALL:
+ try {
+ onUninstalled();
+ } finally {
+ mHost = null;
+ }
+ break;
+
+ case MSG_INPUT_EVENT: {
+ final InputEvent event = (InputEvent)msg.obj;
+ try {
+ if (mInboundInputEventConsistencyVerifier != null) {
+ mInboundInputEventConsistencyVerifier.onInputEvent(event, 0);
+ }
+ onInputEvent(event, msg.arg1);
+ } finally {
+ event.recycle();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ interface Host {
+ public void sendInputEvent(InputEvent event, int policyFlags);
+ }
+}
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index ca1da95..69bde41 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -23,12 +23,6 @@ import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.os.Looper;
import android.os.MessageQueue;
@@ -39,9 +33,11 @@ import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.KeyEvent;
+import android.view.PointerIcon;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
import java.io.File;
import java.io.FileNotFoundException;
@@ -62,7 +58,8 @@ public class InputManager {
private final Context mContext;
private final WindowManagerService mWindowManagerService;
- private static native void nativeInit(Callbacks callbacks, MessageQueue messageQueue);
+ private static native void nativeInit(Context context,
+ Callbacks callbacks, MessageQueue messageQueue);
private static native void nativeStart();
private static native void nativeSetDisplaySize(int displayId, int width, int height);
private static native void nativeSetDisplayOrientation(int displayId, int rotation);
@@ -78,8 +75,10 @@ public class InputManager {
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle, boolean monitor);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+ private static native void nativeSetInputFilterEnabled(boolean enable);
private static native int nativeInjectInputEvent(InputEvent event,
- int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
+ int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
+ int policyFlags);
private static native void nativeSetInputWindows(InputWindow[] windows);
private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
private static native void nativeSetSystemUiVisibility(int visibility);
@@ -117,6 +116,11 @@ public class InputManager {
/** 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;
+ // State for the currently installed input filter.
+ final Object mInputFilterLock = new Object();
+ InputFilter mInputFilter;
+ InputFilterHost mInputFilterHost;
+
public InputManager(Context context, WindowManagerService windowManagerService) {
this.mContext = context;
this.mWindowManagerService = windowManagerService;
@@ -125,7 +129,7 @@ public class InputManager {
Looper looper = windowManagerService.mH.getLooper();
Slog.i(TAG, "Initializing input manager");
- nativeInit(mCallbacks, looper.getQueue());
+ nativeInit(mContext, mCallbacks, looper.getQueue());
}
public void start() {
@@ -268,7 +272,42 @@ public class InputManager {
nativeUnregisterInputChannel(inputChannel);
}
-
+
+ /**
+ * Sets an input filter that will receive all input events before they are dispatched.
+ * The input filter may then reinterpret input events or inject new ones.
+ *
+ * To ensure consistency, the input dispatcher automatically drops all events
+ * in progress whenever an input filter is installed or uninstalled. After an input
+ * filter is uninstalled, it can no longer send input events unless it is reinstalled.
+ * Any events it attempts to send after it has been uninstalled will be dropped.
+ *
+ * @param filter The input filter, or null to remove the current filter.
+ */
+ public void setInputFilter(InputFilter filter) {
+ synchronized (mInputFilterLock) {
+ final InputFilter oldFilter = mInputFilter;
+ if (oldFilter == filter) {
+ return; // nothing to do
+ }
+
+ if (oldFilter != null) {
+ mInputFilter = null;
+ mInputFilterHost.disconnectLocked();
+ mInputFilterHost = null;
+ oldFilter.uninstall();
+ }
+
+ if (filter != null) {
+ mInputFilter = filter;
+ mInputFilterHost = new InputFilterHost();
+ filter.install(mInputFilterHost);
+ }
+
+ nativeSetInputFilterEnabled(filter != null);
+ }
+ }
+
/**
* Injects an input event into the event system on behalf of an application.
* The synchronization mode determines whether the method blocks while waiting for
@@ -304,9 +343,10 @@ public class InputManager {
throw new IllegalArgumentException("timeoutMillis must be positive");
}
- return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
+ return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis,
+ WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
}
-
+
/**
* Gets information about the input device with the specified id.
* @param id The device id.
@@ -370,52 +410,31 @@ public class InputManager {
}
}
- private static final class PointerIcon {
- public Bitmap bitmap;
- public float hotSpotX;
- public float hotSpotY;
-
- public static PointerIcon load(Resources resources, int resourceId) {
- PointerIcon icon = new PointerIcon();
+ private final class InputFilterHost implements InputFilter.Host {
+ private boolean mDisconnected;
- XmlResourceParser parser = resources.getXml(resourceId);
- final int bitmapRes;
- try {
- XmlUtils.beginDocument(parser, "pointer-icon");
-
- TypedArray a = resources.obtainAttributes(
- parser, com.android.internal.R.styleable.PointerIcon);
- bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
- icon.hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
- icon.hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
- a.recycle();
- } catch (Exception ex) {
- Slog.e(TAG, "Exception parsing pointer icon resource.", ex);
- return null;
- } finally {
- parser.close();
- }
+ public void disconnectLocked() {
+ mDisconnected = true;
+ }
- if (bitmapRes == 0) {
- Slog.e(TAG, "<pointer-icon> is missing bitmap attribute");
- return null;
+ public void sendInputEvent(InputEvent event, int policyFlags) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
}
- Drawable drawable = resources.getDrawable(bitmapRes);
- if (!(drawable instanceof BitmapDrawable)) {
- Slog.e(TAG, "<pointer-icon> bitmap attribute must refer to a bitmap drawable");
- return null;
+ synchronized (mInputFilterLock) {
+ if (!mDisconnected) {
+ nativeInjectInputEvent(event, 0, 0, INPUT_EVENT_INJECTION_SYNC_NONE, 0,
+ policyFlags | WindowManagerPolicy.FLAG_FILTERED);
+ }
}
-
- icon.bitmap = ((BitmapDrawable)drawable).getBitmap();
- return icon;
}
}
/*
* Callbacks from native.
*/
- private class Callbacks {
+ private final class Callbacks {
static final String TAG = "InputManager-Callbacks";
private static final boolean DEBUG_VIRTUAL_KEYS = false;
@@ -443,7 +462,19 @@ public class InputManager {
return mWindowManagerService.mInputMonitor.notifyANR(
inputApplicationHandle, inputWindowHandle);
}
-
+
+ @SuppressWarnings("unused")
+ final boolean filterInputEvent(InputEvent event, int policyFlags) {
+ synchronized (mInputFilterLock) {
+ if (mInputFilter != null) {
+ mInputFilter.filterInputEvent(event, policyFlags);
+ return false;
+ }
+ }
+ event.recycle();
+ return true;
+ }
+
@SuppressWarnings("unused")
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
@@ -564,8 +595,7 @@ public class InputManager {
@SuppressWarnings("unused")
public PointerIcon getPointerIcon() {
- return PointerIcon.load(mContext.getResources(),
- com.android.internal.R.drawable.pointer_arrow_icon);
+ return PointerIcon.getDefaultIcon(mContext);
}
}
}
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 57f0799..a3e8be0 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -132,8 +132,8 @@ final class InputMonitor {
// The drag window covers the entire display
inputWindow.frameLeft = 0;
inputWindow.frameTop = 0;
- inputWindow.frameRight = mService.mDisplay.getWidth();
- inputWindow.frameBottom = mService.mDisplay.getHeight();
+ inputWindow.frameRight = mService.mDisplay.getRealWidth();
+ inputWindow.frameBottom = mService.mDisplay.getRealHeight();
// The drag window cannot receive new touches.
inputWindow.touchableRegion.setEmpty();
diff --git a/services/java/com/android/server/wm/ViewServer.java b/services/java/com/android/server/wm/ViewServer.java
index cebd5e7..9fb35b9 100644
--- a/services/java/com/android/server/wm/ViewServer.java
+++ b/services/java/com/android/server/wm/ViewServer.java
@@ -49,7 +49,7 @@ class ViewServer implements Runnable {
// Debug facility
private static final String LOG_TAG = "ViewServer";
- private static final String VALUE_PROTOCOL_VERSION = "3";
+ private static final String VALUE_PROTOCOL_VERSION = "4";
private static final String VALUE_SERVER_VERSION = "4";
// Protocol commands
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 769e423..12a5000 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -63,6 +63,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.BatteryStats;
@@ -391,9 +392,12 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mSystemBooted = false;
int mInitialDisplayWidth = 0;
int mInitialDisplayHeight = 0;
+ int mCurDisplayWidth = 0;
+ int mCurDisplayHeight = 0;
int mRotation = 0;
int mRequestedRotation = 0;
int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ boolean mAltOrientation = false;
int mLastRotationFlags;
ArrayList<IRotationWatcher> mRotationWatchers
= new ArrayList<IRotationWatcher>();
@@ -581,7 +585,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
final Configuration mTempConfiguration = new Configuration();
- int mScreenLayout = Configuration.SCREENLAYOUT_SIZE_UNDEFINED;
// The frame use to limit the size of the app running in compatibility mode.
Rect mCompatibleScreenFrame = new Rect();
@@ -1419,8 +1422,8 @@ public class WindowManagerService extends IWindowManager.Stub
int adjustWallpaperWindowsLocked() {
int changed = 0;
- final int dw = mDisplay.getWidth();
- final int dh = mDisplay.getHeight();
+ final int dw = mCurDisplayWidth;
+ final int dh = mCurDisplayHeight;
// First find top-most window that has asked to be on top of the
// wallpaper; all wallpapers go behind it.
@@ -1838,8 +1841,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
boolean updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
- final int dw = mDisplay.getWidth();
- final int dh = mDisplay.getHeight();
+ final int dw = mCurDisplayWidth;
+ final int dh = mCurDisplayHeight;
boolean changed = false;
@@ -1879,8 +1882,8 @@ public class WindowManagerService extends IWindowManager.Stub
void updateWallpaperVisibilityLocked() {
final boolean visible = isWallpaperVisible(mWallpaperTarget);
- final int dw = mDisplay.getWidth();
- final int dh = mDisplay.getHeight();
+ final int dw = mCurDisplayWidth;
+ final int dh = mCurDisplayHeight;
int curTokenIndex = mWallpaperTokens.size();
while (curTokenIndex > 0) {
@@ -2682,8 +2685,7 @@ public class WindowManagerService extends IWindowManager.Stub
configChanged = updateOrientationFromAppTokensLocked(false);
performLayoutAndPlaceSurfacesLocked();
if (displayed && win.mIsWallpaper) {
- updateWallpaperOffsetLocked(win, mDisplay.getWidth(),
- mDisplay.getHeight(), false);
+ updateWallpaperOffsetLocked(win, mCurDisplayWidth, mCurDisplayHeight, false);
}
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
@@ -4606,6 +4608,10 @@ public class WindowManagerService extends IWindowManager.Stub
return mInputManager.monitorInput(inputChannelName);
}
+ public void setInputFilter(InputFilter filter) {
+ mInputManager.setInputFilter(filter);
+ }
+
public InputDevice getInputDevice(int deviceId) {
return mInputManager.getInputDevice(deviceId);
}
@@ -4755,8 +4761,8 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
long ident = Binder.clearCallingIdentity();
- dw = mPolicy.getNonDecorDisplayWidth(mDisplay.getWidth());
- dh = mPolicy.getNonDecorDisplayHeight(mDisplay.getHeight());
+ dw = mPolicy.getNonDecorDisplayWidth(mCurDisplayWidth);
+ dh = mPolicy.getNonDecorDisplayHeight(mCurDisplayHeight);
int aboveAppLayer = mPolicy.windowTypeToLayerLw(
WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER
@@ -4950,7 +4956,52 @@ public class WindowManagerService extends IWindowManager.Stub
rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation,
mRotation, mDisplayEnabled);
if (DEBUG_ORIENTATION) Slog.v(TAG, "new rotation is set to " + rotation);
+
+ int desiredRotation = rotation;
+ int lockedRotation = mPolicy.getLockedRotationLw();
+ if (lockedRotation >= 0 && rotation != lockedRotation) {
+ // We are locked in a rotation but something is requesting
+ // a different rotation... we will either keep the locked
+ // rotation if it results in the same orientation, or have to
+ // switch into an emulated orientation mode.
+
+ // First, we know that our rotation is actually going to be
+ // the locked rotation.
+ rotation = lockedRotation;
+
+ // Now the difference between the desired and lockedRotation
+ // may mean that the orientation is different... if that is
+ // not the case, we can just make the desired rotation be the
+ // same as the new locked rotation.
+ switch (lockedRotation) {
+ case Surface.ROTATION_0:
+ if (rotation == Surface.ROTATION_180) {
+ desiredRotation = lockedRotation;
+ }
+ break;
+ case Surface.ROTATION_90:
+ if (rotation == Surface.ROTATION_270) {
+ desiredRotation = lockedRotation;
+ }
+ break;
+ case Surface.ROTATION_180:
+ if (rotation == Surface.ROTATION_0) {
+ desiredRotation = lockedRotation;
+ }
+ break;
+ case Surface.ROTATION_270:
+ if (rotation == Surface.ROTATION_90) {
+ desiredRotation = lockedRotation;
+ }
+ break;
+ }
+ }
+
changed = mDisplayEnabled && mRotation != rotation;
+ if (mAltOrientation != (rotation != desiredRotation)) {
+ changed = true;
+ mAltOrientation = rotation != desiredRotation;
+ }
if (changed) {
if (DEBUG_ORIENTATION) Slog.v(TAG,
@@ -4996,6 +5047,7 @@ public class WindowManagerService extends IWindowManager.Stub
Surface.setOrientation(0, rotation, animFlags);
}
}
+
for (int i=mWindows.size()-1; i>=0; i--) {
WindowState w = mWindows.get(i);
if (w.mSurface != null) {
@@ -5436,8 +5488,32 @@ public class WindowManagerService extends IWindowManager.Stub
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
|| mRotation == Surface.ROTATION_270);
- final int dw = rotated ? mInitialDisplayHeight : mInitialDisplayWidth;
- final int dh = rotated ? mInitialDisplayWidth : mInitialDisplayHeight;
+ final int realdw = rotated ? mInitialDisplayHeight : mInitialDisplayWidth;
+ final int realdh = rotated ? mInitialDisplayWidth : mInitialDisplayHeight;
+
+ if (mAltOrientation) {
+ mCurDisplayWidth = realdw;
+ mCurDisplayHeight = realdh;
+ if (realdw > realdh) {
+ // Turn landscape into portrait.
+ int maxw = (int)(realdh/1.3f);
+ if (maxw < realdw) {
+ mCurDisplayWidth = maxw;
+ }
+ } else {
+ // Turn portrait into landscape.
+ int maxh = (int)(realdw/1.3f);
+ if (maxh < realdh) {
+ mCurDisplayHeight = maxh;
+ }
+ }
+ } else {
+ mCurDisplayWidth = realdw;
+ mCurDisplayHeight = realdh;
+ }
+
+ final int dw = mCurDisplayWidth;
+ final int dh = mCurDisplayHeight;
int orientation = Configuration.ORIENTATION_SQUARE;
if (dw < dh) {
@@ -5448,66 +5524,70 @@ public class WindowManagerService extends IWindowManager.Stub
config.orientation = orientation;
DisplayMetrics dm = new DisplayMetrics();
- mDisplay.getMetrics(dm);
- dm.realWidthPixels = mPolicy.getNonDecorDisplayWidth(dm.realWidthPixels);
- dm.realHeightPixels = mPolicy.getNonDecorDisplayHeight(dm.realHeightPixels);
+ mDisplay.getRealMetrics(dm);
+
+ // Override display width and height with what we are computing,
+ // to be sure they remain consistent.
+ dm.widthPixels = mPolicy.getNonDecorDisplayWidth(dw);
+ dm.heightPixels = mPolicy.getNonDecorDisplayHeight(dh);
+
mCompatibleScreenScale = CompatibilityInfo.updateCompatibleScreenFrame(
dm, mCompatibleScreenFrame, null);
- if (mScreenLayout == Configuration.SCREENLAYOUT_SIZE_UNDEFINED) {
- // Note we only do this once because at this point we don't
- // expect the screen to change in this way at runtime, and want
- // to avoid all of this computation for every config change.
- int longSize = dw;
- int shortSize = dh;
- if (longSize < shortSize) {
- int tmp = longSize;
- longSize = shortSize;
- shortSize = tmp;
- }
- longSize = (int)(longSize/dm.density);
- shortSize = (int)(shortSize/dm.density);
-
- // These semi-magic numbers define our compatibility modes for
- // applications with different screens. These are guarantees to
- // app developers about the space they can expect for a particular
- // configuration. DO NOT CHANGE!
- if (longSize < 470) {
- // This is shorter than an HVGA normal density screen (which
- // is 480 pixels on its long side).
- mScreenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL
- | Configuration.SCREENLAYOUT_LONG_NO;
+ config.screenWidthDp = (int)(dm.widthPixels / dm.density);
+ config.screenHeightDp = (int)(dm.heightPixels / dm.density);
+
+ // Compute the screen layout size class.
+ int screenLayout;
+ int longSize = dw;
+ int shortSize = dh;
+ if (longSize < shortSize) {
+ int tmp = longSize;
+ longSize = shortSize;
+ shortSize = tmp;
+ }
+ longSize = (int)(longSize/dm.density);
+ shortSize = (int)(shortSize/dm.density);
+
+ // These semi-magic numbers define our compatibility modes for
+ // applications with different screens. These are guarantees to
+ // app developers about the space they can expect for a particular
+ // configuration. DO NOT CHANGE!
+ if (longSize < 470) {
+ // This is shorter than an HVGA normal density screen (which
+ // is 480 pixels on its long side).
+ screenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL
+ | Configuration.SCREENLAYOUT_LONG_NO;
+ } else {
+ // What size is this screen screen?
+ if (longSize >= 960 && shortSize >= 720) {
+ // 1.5xVGA or larger screens at medium density are the point
+ // at which we consider it to be an extra large screen.
+ screenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ } else if (longSize >= 640 && shortSize >= 480) {
+ // VGA or larger screens at medium density are the point
+ // at which we consider it to be a large screen.
+ screenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
} else {
- // What size is this screen screen?
- if (longSize >= 960 && shortSize >= 720) {
- // 1.5xVGA or larger screens at medium density are the point
- // at which we consider it to be an extra large screen.
- 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;
- } else {
- mScreenLayout = Configuration.SCREENLAYOUT_SIZE_NORMAL;
- }
-
- // If this screen is wider than normal HVGA, or taller
- // than FWVGA, then for old apps we want to run in size
- // compatibility mode.
- if (shortSize > 321 || longSize > 570) {
- mScreenLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED;
- }
+ screenLayout = Configuration.SCREENLAYOUT_SIZE_NORMAL;
+ }
- // Is this a long screen?
- if (((longSize*3)/5) >= (shortSize-1)) {
- // Anything wider than WVGA (5:3) is considering to be long.
- mScreenLayout |= Configuration.SCREENLAYOUT_LONG_YES;
- } else {
- mScreenLayout |= Configuration.SCREENLAYOUT_LONG_NO;
- }
+ // If this screen is wider than normal HVGA, or taller
+ // than FWVGA, then for old apps we want to run in size
+ // compatibility mode.
+ if (shortSize > 321 || longSize > 570) {
+ screenLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED;
+ }
+
+ // Is this a long screen?
+ if (((longSize*3)/5) >= (shortSize-1)) {
+ // Anything wider than WVGA (5:3) is considering to be long.
+ screenLayout |= Configuration.SCREENLAYOUT_LONG_YES;
+ } else {
+ screenLayout |= Configuration.SCREENLAYOUT_LONG_NO;
}
}
- config.screenLayout = mScreenLayout;
+ config.screenLayout = screenLayout;
// Determine whether a hard keyboard is available and enabled.
boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
@@ -5846,9 +5926,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mDisplay = wm.getDefaultDisplay();
- mInitialDisplayWidth = mDisplay.getWidth();
- mInitialDisplayHeight = mDisplay.getHeight();
- mInputManager.setDisplaySize(0, mDisplay.getRealWidth(), mDisplay.getRealHeight());
+ mInitialDisplayWidth = mCurDisplayWidth = mDisplay.getRealWidth();
+ mInitialDisplayHeight = mCurDisplayHeight = mDisplay.getRealHeight();
+ mInputManager.setDisplaySize(0, mDisplay.getRawWidth(), mDisplay.getRawHeight());
+ mPolicy.setInitialDisplaySize(mInitialDisplayWidth, mInitialDisplayHeight);
}
try {
@@ -6342,6 +6423,21 @@ public class WindowManagerService extends IWindowManager.Stub
return false;
}
+ public void getDisplaySize(Point size) {
+ synchronized(mWindowMap) {
+ size.x = mCurDisplayWidth;
+ size.y = mCurDisplayHeight;
+ }
+ }
+
+ public int getMaximumSizeDimension() {
+ synchronized(mWindowMap) {
+ // Do this based on the raw screen size, until we are smarter.
+ return mInitialDisplayWidth > mInitialDisplayHeight
+ ? mInitialDisplayWidth : mInitialDisplayHeight;
+ }
+ }
+
// -------------------------------------------------------------
// Internals
// -------------------------------------------------------------
@@ -6584,8 +6680,8 @@ public class WindowManagerService extends IWindowManager.Stub
mLayoutNeeded = false;
- final int dw = mDisplay.getWidth();
- final int dh = mDisplay.getHeight();
+ final int dw = mCurDisplayWidth;
+ final int dh = mCurDisplayHeight;
final int innerDw = mPolicy.getNonDecorDisplayWidth(dw);
final int innerDh = mPolicy.getNonDecorDisplayHeight(dh);
@@ -6709,8 +6805,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
final long currentTime = SystemClock.uptimeMillis();
- final int dw = mDisplay.getWidth();
- final int dh = mDisplay.getHeight();
+ final int dw = mCurDisplayWidth;
+ final int dh = mCurDisplayHeight;
final int innerDw = mPolicy.getNonDecorDisplayWidth(dw);
final int innerDh = mPolicy.getNonDecorDisplayHeight(dh);
@@ -8707,24 +8803,25 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig);
pw.print(" mRotation="); pw.print(mRotation);
- pw.print(", mForcedAppOrientation="); pw.print(mForcedAppOrientation);
- pw.print(", mRequestedRotation="); pw.println(mRequestedRotation);
+ pw.print(" mForcedAppOrientation="); pw.print(mForcedAppOrientation);
+ pw.print(" mRequestedRotation="); pw.print(mRequestedRotation);
+ pw.print(" mAltOrientation="); pw.println(mAltOrientation);
pw.print(" mDeferredRotation="); pw.print(mDeferredRotation);
- pw.print(", mDeferredRotationAnimFlags="); pw.print(mDeferredRotationAnimFlags);
+ pw.print(", mDeferredRotationAnimFlags="); pw.println(mDeferredRotationAnimFlags);
pw.print(" mAnimationPending="); pw.print(mAnimationPending);
pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale);
pw.print(" mTransitionWindowAnimationScale="); pw.println(mTransitionAnimationScale);
pw.print(" mNextAppTransition=0x");
pw.print(Integer.toHexString(mNextAppTransition));
- pw.print(", mAppTransitionReady="); pw.print(mAppTransitionReady);
- pw.print(", mAppTransitionRunning="); pw.print(mAppTransitionRunning);
- pw.print(", mAppTransitionTimeout="); pw.println( mAppTransitionTimeout);
+ pw.print(" mAppTransitionReady="); pw.println(mAppTransitionReady);
+ pw.print(" mAppTransitionRunning="); pw.print(mAppTransitionRunning);
+ pw.print(" mAppTransitionTimeout="); pw.println( mAppTransitionTimeout);
if (mNextAppTransitionPackage != null) {
pw.print(" mNextAppTransitionPackage=");
pw.print(mNextAppTransitionPackage);
- pw.print(", mNextAppTransitionEnter=0x");
+ pw.print(" mNextAppTransitionEnter=0x");
pw.print(Integer.toHexString(mNextAppTransitionEnter));
- pw.print(", mNextAppTransitionExit=0x");
+ pw.print(" mNextAppTransitionExit=0x");
pw.print(Integer.toHexString(mNextAppTransitionExit));
}
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);
@@ -8742,8 +8839,13 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mToBottomApps="); pw.println(mToBottomApps);
}
if (mDisplay != null) {
- pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
- pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
+ pw.print(" Display: init="); pw.print(mInitialDisplayWidth); pw.print("x");
+ pw.print(mInitialDisplayHeight); pw.print(" cur=");
+ pw.print(mCurDisplayWidth); pw.print("x"); pw.print(mCurDisplayHeight);
+ pw.print(" real="); pw.print(mDisplay.getRealWidth());
+ pw.print("x"); pw.print(mDisplay.getRealHeight());
+ pw.print(" raw="); pw.print(mDisplay.getRawWidth());
+ pw.print("x"); pw.println(mDisplay.getRawHeight());
} else {
pw.println(" NO DISPLAY");
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 72049ec..2014e9d 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -483,8 +483,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
- mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getWidth(),
- mService.mDisplay.getHeight(), false);
+ mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getRealWidth(),
+ mService.mDisplay.getRealHeight(), false);
}
if (WindowManagerService.localLOGV) {
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index be37d5d..4e93fe2 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -34,7 +34,7 @@ LOCAL_SHARED_LIBRARIES := \
libui \
libinput \
libskia \
- libsurfaceflinger_client \
+ libgui \
libusbhost
ifeq ($(TARGET_SIMULATOR),true)
diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp
index aa8c9b3..c9a702a 100644
--- a/services/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/jni/com_android_server_AlarmManagerService.cpp
@@ -2,16 +2,16 @@
**
** Copyright 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
+** 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
+** 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
+** 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.
*/
@@ -84,7 +84,7 @@ static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jin
struct timespec ts;
ts.tv_sec = seconds;
ts.tv_nsec = nanoseconds;
-
+
int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);
if (result < 0)
{
@@ -97,18 +97,18 @@ static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject
{
#ifdef HAVE_ANDROID_OS
int result = 0;
-
+
do
{
result = ioctl(fd, ANDROID_ALARM_WAIT);
} while (result < 0 && errno == EINTR);
-
+
if (result < 0)
{
LOGE("Unable to wait on alarm: %s\n", strerror(errno));
return 0;
}
-
+
return result;
#endif
}
@@ -124,14 +124,6 @@ static JNINativeMethod sMethods[] = {
int register_android_server_AlarmManagerService(JNIEnv* env)
{
- jclass clazz = env->FindClass("com/android/server/AlarmManagerService");
-
- if (clazz == NULL)
- {
- LOGE("Can't find com/android/server/AlarmManagerService");
- return -1;
- }
-
return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService",
sMethods, NELEM(sMethods));
}
diff --git a/services/jni/com_android_server_InputApplication.cpp b/services/jni/com_android_server_InputApplication.cpp
index e64ec4e..1f80242 100644
--- a/services/jni/com_android_server_InputApplication.cpp
+++ b/services/jni/com_android_server_InputApplication.cpp
@@ -26,8 +26,6 @@
namespace android {
static struct {
- jclass clazz;
-
jfieldID inputApplicationHandle;
jfieldID name;
jfieldID dispatchingTimeoutNanos;
@@ -69,25 +67,25 @@ void android_server_InputApplication_toNative(
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#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_InputApplication(JNIEnv* env) {
- FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/wm/InputApplication");
+ jclass clazz;
+ FIND_CLASS(clazz, "com/android/server/wm/InputApplication");
GET_FIELD_ID(gInputApplicationClassInfo.inputApplicationHandle,
- gInputApplicationClassInfo.clazz,
+ clazz,
"inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;");
- GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz,
+ GET_FIELD_ID(gInputApplicationClassInfo.name, clazz,
"name", "Ljava/lang/String;");
GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos,
- gInputApplicationClassInfo.clazz,
+ clazz,
"dispatchingTimeoutNanos", "J");
return 0;
}
diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp
index 3a1214f..9516964 100644
--- a/services/jni/com_android_server_InputApplicationHandle.cpp
+++ b/services/jni/com_android_server_InputApplicationHandle.cpp
@@ -26,8 +26,6 @@
namespace android {
static struct {
- jclass clazz;
-
jfieldID ptr;
} gInputApplicationHandleClassInfo;
@@ -98,8 +96,7 @@ static JNINativeMethod gInputApplicationHandleMethods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
@@ -110,9 +107,10 @@ int register_android_server_InputApplicationHandle(JNIEnv* env) {
gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
- FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/wm/InputApplicationHandle");
+ jclass clazz;
+ FIND_CLASS(clazz, "com/android/server/wm/InputApplicationHandle");
- GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, gInputApplicationHandleClassInfo.clazz,
+ GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz,
"ptr", "I");
return 0;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 80dddc2..1f10d9c 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -36,11 +36,13 @@
#include <input/InputManager.h>
#include <input/PointerController.h>
+#include <input/SpriteController.h>
#include <android_os_MessageQueue.h>
#include <android_view_KeyEvent.h>
#include <android_view_MotionEvent.h>
#include <android_view_InputChannel.h>
+#include <android_view_PointerIcon.h>
#include <android/graphics/GraphicsJNI.h>
#include "com_android_server_PowerManagerService.h"
@@ -52,12 +54,11 @@
namespace android {
static struct {
- jclass clazz;
-
jmethodID notifyConfigurationChanged;
jmethodID notifyLidSwitchChanged;
jmethodID notifyInputChannelBroken;
jmethodID notifyANR;
+ jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptMotionBeforeQueueingWhenScreenOff;
jmethodID interceptKeyBeforeDispatching;
@@ -95,21 +96,11 @@ static struct {
} gInputDeviceClassInfo;
static struct {
- jclass clazz;
-
jfieldID touchscreen;
jfieldID keyboard;
jfieldID navigation;
} gConfigurationClassInfo;
-static struct {
- jclass clazz;
-
- jfieldID bitmap;
- jfieldID hotSpotX;
- jfieldID hotSpotY;
-} gPointerIconClassInfo;
-
// --- Global functions ---
@@ -131,17 +122,30 @@ static jobject getInputWindowHandleObjLocalRef(JNIEnv* env,
getInputWindowHandleObjLocalRef(env);
}
+static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t style,
+ SpriteIcon* outSpriteIcon) {
+ PointerIcon pointerIcon;
+ status_t status = android_view_PointerIcon_loadSystemIcon(env,
+ contextObj, style, &pointerIcon);
+ if (!status) {
+ pointerIcon.bitmap.copyTo(&outSpriteIcon->bitmap, SkBitmap::kARGB_8888_Config);
+ outSpriteIcon->hotSpotX = pointerIcon.hotSpotX;
+ outSpriteIcon->hotSpotY = pointerIcon.hotSpotY;
+ }
+}
+
// --- NativeInputManager ---
class NativeInputManager : public virtual RefBase,
public virtual InputReaderPolicyInterface,
- public virtual InputDispatcherPolicyInterface {
+ public virtual InputDispatcherPolicyInterface,
+ public virtual PointerControllerPolicyInterface {
protected:
virtual ~NativeInputManager();
public:
- NativeInputManager(jobject callbacksObj, const sp<Looper>& looper);
+ NativeInputManager(jobject contextObj, jobject callbacksObj, const sp<Looper>& looper);
inline sp<InputManager> getInputManager() const { return mInputManager; }
@@ -180,6 +184,7 @@ public:
virtual nsecs_t getKeyRepeatTimeout();
virtual nsecs_t getKeyRepeatDelay();
virtual int32_t getMaxEventsPerSecond();
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
@@ -190,9 +195,14 @@ public:
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid);
+ /* --- PointerControllerPolicyInterface implementation --- */
+
+ virtual void loadPointerResources(PointerResources* outResources);
+
private:
sp<InputManager> mInputManager;
+ jobject mContextObj;
jobject mCallbacksObj;
sp<Looper> mLooper;
@@ -217,12 +227,16 @@ private:
// System UI visibility.
int32_t systemUiVisibility;
+ // Sprite controller singleton, created on first use.
+ sp<SpriteController> spriteController;
+
// Pointer controller singleton, created and destroyed as needed.
wp<PointerController> pointerController;
} mLocked;
- void updateInactivityFadeDelayLocked(const sp<PointerController>& controller);
+ void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
+ void ensureSpriteControllerLocked();
// Power manager interactions.
bool isScreenOn();
@@ -237,13 +251,15 @@ private:
-NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& looper) :
+NativeInputManager::NativeInputManager(jobject contextObj,
+ jobject callbacksObj, const sp<Looper>& looper) :
mLooper(looper),
mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
mKeyRepeatTimeout(-1), mKeyRepeatDelay(-1),
mMaxEventsPerSecond(-1) {
JNIEnv* env = jniEnv();
+ mContextObj = env->NewGlobalRef(contextObj);
mCallbacksObj = env->NewGlobalRef(callbacksObj);
{
@@ -262,6 +278,7 @@ NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& l
NativeInputManager::~NativeInputManager() {
JNIEnv* env = jniEnv();
+ env->DeleteGlobalRef(mContextObj);
env->DeleteGlobalRef(mCallbacksObj);
}
@@ -285,9 +302,13 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c
void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
if (displayId == 0) {
- AutoMutex _l(mLock);
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mLocked.displayWidth == width && mLocked.displayHeight == height) {
+ return;
+ }
- if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
mLocked.displayWidth = width;
mLocked.displayHeight = height;
@@ -295,7 +316,7 @@ void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_
if (controller != NULL) {
controller->setDisplaySize(width, height);
}
- }
+ } // release lock
}
}
@@ -423,38 +444,46 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32
sp<PointerController> controller = mLocked.pointerController.promote();
if (controller == NULL) {
- JNIEnv* env = jniEnv();
- jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer);
- if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
- layer = -1;
- }
+ ensureSpriteControllerLocked();
- controller = new PointerController(mLooper, layer);
+ controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight);
controller->setDisplayOrientation(mLocked.displayOrientation);
- jobject iconObj = env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getPointerIcon);
- if (!checkAndClearExceptionFromCallback(env, "getPointerIcon") && iconObj) {
- jfloat iconHotSpotX = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotX);
- jfloat iconHotSpotY = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotY);
- jobject iconBitmapObj = env->GetObjectField(iconObj, gPointerIconClassInfo.bitmap);
- if (iconBitmapObj) {
- SkBitmap* iconBitmap = GraphicsJNI::getNativeBitmap(env, iconBitmapObj);
- if (iconBitmap) {
- controller->setPointerIcon(iconBitmap, iconHotSpotX, iconHotSpotY);
- }
- env->DeleteLocalRef(iconBitmapObj);
+ JNIEnv* env = jniEnv();
+ jobject pointerIconObj = env->CallObjectMethod(mCallbacksObj,
+ gCallbacksClassInfo.getPointerIcon);
+ if (!checkAndClearExceptionFromCallback(env, "getPointerIcon")) {
+ PointerIcon pointerIcon;
+ status_t status = android_view_PointerIcon_load(env, pointerIconObj,
+ mContextObj, &pointerIcon);
+ if (!status && !pointerIcon.isNullIcon()) {
+ controller->setPointerIcon(SpriteIcon(pointerIcon.bitmap,
+ pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ } else {
+ controller->setPointerIcon(SpriteIcon());
}
- env->DeleteLocalRef(iconObj);
+ env->DeleteLocalRef(pointerIconObj);
}
- updateInactivityFadeDelayLocked(controller);
+ updateInactivityTimeoutLocked(controller);
}
return controller;
}
+void NativeInputManager::ensureSpriteControllerLocked() {
+ if (mLocked.spriteController == NULL) {
+ JNIEnv* env = jniEnv();
+ jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer);
+ if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
+ layer = -1;
+ }
+ mLocked.spriteController = new SpriteController(mLooper, layer);
+ }
+}
+
void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
int32_t switchValue, uint32_t policyFlags) {
#if DEBUG_INPUT_DISPATCHER_POLICY
@@ -606,6 +635,7 @@ void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationO
android_server_InputApplication_toNative(env, applicationObj, &application);
if (application.inputApplicationHandle != NULL) {
mInputManager->getDispatcher()->setFocusedApplication(&application);
+ return;
}
}
mInputManager->getDispatcher()->setFocusedApplication(NULL);
@@ -623,16 +653,16 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
sp<PointerController> controller = mLocked.pointerController.promote();
if (controller != NULL) {
- updateInactivityFadeDelayLocked(controller);
+ updateInactivityTimeoutLocked(controller);
}
}
}
-void NativeInputManager::updateInactivityFadeDelayLocked(const sp<PointerController>& controller) {
+void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) {
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
- controller->setInactivityFadeDelay(lightsOut
- ? PointerController::INACTIVITY_FADE_DELAY_SHORT
- : PointerController::INACTIVITY_FADE_DELAY_NORMAL);
+ controller->setInactivityTimeout(lightsOut
+ ? PointerController::INACTIVITY_TIMEOUT_SHORT
+ : PointerController::INACTIVITY_TIMEOUT_NORMAL);
}
bool NativeInputManager::isScreenOn() {
@@ -643,6 +673,38 @@ bool NativeInputManager::isScreenBright() {
return android_server_PowerManagerService_isScreenBright();
}
+bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+ jobject inputEventObj;
+
+ JNIEnv* env = jniEnv();
+ switch (inputEvent->getType()) {
+ case AINPUT_EVENT_TYPE_KEY:
+ inputEventObj = android_view_KeyEvent_fromNative(env,
+ static_cast<const KeyEvent*>(inputEvent));
+ break;
+ case AINPUT_EVENT_TYPE_MOTION:
+ inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+ static_cast<const MotionEvent*>(inputEvent));
+ break;
+ default:
+ return true; // dispatch the event normally
+ }
+
+ if (!inputEventObj) {
+ LOGE("Failed to obtain input event object for filterInputEvent.");
+ return true; // dispatch the event normally
+ }
+
+ // The callee is responsible for recycling the event.
+ jboolean pass = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.filterInputEvent,
+ inputEventObj, policyFlags);
+ if (checkAndClearExceptionFromCallback(env, "filterInputEvent")) {
+ pass = true;
+ }
+ env->DeleteLocalRef(inputEventObj);
+ return pass;
+}
+
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
uint32_t& policyFlags) {
// Policy:
@@ -794,7 +856,9 @@ bool NativeInputManager::dispatchUnhandledKey(const sp<InputWindowHandle>& input
jobject fallbackKeyEventObj = env->CallObjectMethod(mCallbacksObj,
gCallbacksClassInfo.dispatchUnhandledKey,
inputWindowHandleObj, keyEventObj, policyFlags);
- checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey");
+ if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) {
+ fallbackKeyEventObj = NULL;
+ }
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
@@ -825,10 +889,23 @@ bool NativeInputManager::checkInjectEventsPermissionNonReentrant(
JNIEnv* env = jniEnv();
jboolean result = env->CallBooleanMethod(mCallbacksObj,
gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
- checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
+ if (checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission")) {
+ result = false;
+ }
return result;
}
+void NativeInputManager::loadPointerResources(PointerResources* outResources) {
+ JNIEnv* env = jniEnv();
+
+ loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_HOVER,
+ &outResources->spotHover);
+ loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_TOUCH,
+ &outResources->spotTouch);
+ loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_ANCHOR,
+ &outResources->spotAnchor);
+}
+
// ----------------------------------------------------------------------------
@@ -844,10 +921,10 @@ static bool checkInputManagerUnitialized(JNIEnv* env) {
}
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
- jobject callbacks, jobject messageQueueObj) {
+ jobject contextObj, jobject callbacksObj, jobject messageQueueObj) {
if (gNativeInputManager == NULL) {
sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
- gNativeInputManager = new NativeInputManager(callbacks, looper);
+ gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);
} else {
LOGE("Input manager already initialized.");
jniThrowRuntimeException(env, "Input manager already initialized.");
@@ -1006,9 +1083,18 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env
}
}
+static void android_server_InputManager_nativeSetInputFilterEnabled(JNIEnv* env, jclass clazz,
+ jboolean enabled) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->getInputManager()->getDispatcher()->setInputFilterEnabled(enabled);
+}
+
static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jclass clazz,
jobject inputEventObj, jint injectorPid, jint injectorUid,
- jint syncMode, jint timeoutMillis) {
+ jint syncMode, jint timeoutMillis, jint policyFlags) {
if (checkInputManagerUnitialized(env)) {
return INPUT_EVENT_INJECTION_FAILED;
}
@@ -1022,17 +1108,18 @@ static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jcla
}
return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
- & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
+ & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
+ uint32_t(policyFlags));
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
- MotionEvent motionEvent;
- status_t status = android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent);
- if (status) {
+ const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
+ if (!motionEvent) {
jniThrowRuntimeException(env, "Could not read contents of MotionEvent object.");
return INPUT_EVENT_INJECTION_FAILED;
}
return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
- & motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
+ motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
+ uint32_t(policyFlags));
} else {
jniThrowRuntimeException(env, "Invalid input event type.");
return INPUT_EVENT_INJECTION_FAILED;
@@ -1181,7 +1268,8 @@ static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz)
static JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
- { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
+ { "nativeInit", "(Landroid/content/Context;"
+ "Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
(void*) android_server_InputManager_nativeInit },
{ "nativeStart", "()V",
(void*) android_server_InputManager_nativeStart },
@@ -1202,7 +1290,9 @@ static JNINativeMethod gInputManagerMethods[] = {
(void*) android_server_InputManager_nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeUnregisterInputChannel },
- { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I",
+ { "nativeSetInputFilterEnabled", "(Z)V",
+ (void*) android_server_InputManager_nativeSetInputFilterEnabled },
+ { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIIII)I",
(void*) android_server_InputManager_nativeInjectInputEvent },
{ "nativeSetInputWindows", "([Lcom/android/server/wm/InputWindow;)V",
(void*) android_server_InputManager_nativeSetInputWindows },
@@ -1226,8 +1316,7 @@ static JNINativeMethod gInputManagerMethods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
@@ -1244,77 +1333,85 @@ int register_android_server_InputManager(JNIEnv* env) {
// Callbacks
- FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/wm/InputManager$Callbacks");
+ jclass clazz;
+ FIND_CLASS(clazz, "com/android/server/wm/InputManager$Callbacks");
- GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, clazz,
"notifyConfigurationChanged", "(J)V");
- GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, clazz,
"notifyLidSwitchChanged", "(JZ)V");
- GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, clazz,
"notifyInputChannelBroken", "(Lcom/android/server/wm/InputWindowHandle;)V");
- GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.notifyANR, clazz,
"notifyANR",
"(Lcom/android/server/wm/InputApplicationHandle;Lcom/android/server/wm/InputWindowHandle;)J");
- GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.filterInputEvent, clazz,
+ "filterInputEvent", "(Landroid/view/InputEvent;I)Z");
+
+ GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, clazz,
"interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I");
GET_METHOD_ID(gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff,
- gCallbacksClassInfo.clazz,
+ clazz,
"interceptMotionBeforeQueueingWhenScreenOff", "(I)I");
- GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, clazz,
"interceptKeyBeforeDispatching",
"(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Z");
- GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, clazz,
"dispatchUnhandledKey",
"(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
- GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, clazz,
"checkInjectEventsPermission", "(II)Z");
- GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, clazz,
"filterTouchEvents", "()Z");
- GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, clazz,
"filterJumpyTouchEvents", "()Z");
- GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyQuietTimeMillis, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyQuietTimeMillis, clazz,
"getVirtualKeyQuietTimeMillis", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
- GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatTimeout, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatTimeout, clazz,
"getKeyRepeatTimeout", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz,
"getKeyRepeatDelay", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz,
"getMaxEventsPerSecond", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getPointerLayer, gCallbacksClassInfo.clazz,
+ GET_METHOD_ID(gCallbacksClassInfo.getPointerLayer, clazz,
"getPointerLayer", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz,
- "getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;");
+ GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, clazz,
+ "getPointerIcon", "()Landroid/view/PointerIcon;");
// KeyEvent
FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
+ gKeyEventClassInfo.clazz = jclass(env->NewGlobalRef(gKeyEventClassInfo.clazz));
+
// MotionEvent
FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
+ gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz));
// InputDevice
FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
+ gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz));
GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz,
"<init>", "()V");
@@ -1336,30 +1433,17 @@ int register_android_server_InputManager(JNIEnv* env) {
// Configuration
- FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration");
+ FIND_CLASS(clazz, "android/content/res/Configuration");
- GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.touchscreen, clazz,
"touchscreen", "I");
- GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.keyboard, clazz,
"keyboard", "I");
- GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.navigation, clazz,
"navigation", "I");
- // PointerIcon
-
- FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/wm/InputManager$PointerIcon");
-
- GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz,
- "bitmap", "Landroid/graphics/Bitmap;");
-
- GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, gPointerIconClassInfo.clazz,
- "hotSpotX", "F");
-
- GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, gPointerIconClassInfo.clazz,
- "hotSpotY", "F");
-
return 0;
}
diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp
index d36c010..012ce21 100644
--- a/services/jni/com_android_server_InputWindow.cpp
+++ b/services/jni/com_android_server_InputWindow.cpp
@@ -28,8 +28,6 @@
namespace android {
static struct {
- jclass clazz;
-
jfieldID inputWindowHandle;
jfieldID inputChannel;
jfieldID name;
@@ -139,74 +137,74 @@ void android_server_InputWindow_toNative(
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#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_InputWindow(JNIEnv* env) {
- FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/wm/InputWindow");
+ jclass clazz;
+ FIND_CLASS(clazz, "com/android/server/wm/InputWindow");
- GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, clazz,
"inputWindowHandle", "Lcom/android/server/wm/InputWindowHandle;");
- GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.inputChannel, clazz,
"inputChannel", "Landroid/view/InputChannel;");
- GET_FIELD_ID(gInputWindowClassInfo.name, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.name, clazz,
"name", "Ljava/lang/String;");
- GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, clazz,
"layoutParamsFlags", "I");
- GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, clazz,
"layoutParamsType", "I");
- GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, clazz,
"dispatchingTimeoutNanos", "J");
- GET_FIELD_ID(gInputWindowClassInfo.frameLeft, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.frameLeft, clazz,
"frameLeft", "I");
- GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.frameTop, clazz,
"frameTop", "I");
- GET_FIELD_ID(gInputWindowClassInfo.frameRight, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.frameRight, clazz,
"frameRight", "I");
- GET_FIELD_ID(gInputWindowClassInfo.frameBottom, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.frameBottom, clazz,
"frameBottom", "I");
- GET_FIELD_ID(gInputWindowClassInfo.scaleFactor, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.scaleFactor, clazz,
"scaleFactor", "F");
- GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, clazz,
"touchableRegion", "Landroid/graphics/Region;");
- GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.visible, clazz,
"visible", "Z");
- GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, clazz,
"canReceiveKeys", "Z");
- GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.hasFocus, clazz,
"hasFocus", "Z");
- GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, clazz,
"hasWallpaper", "Z");
- GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.paused, clazz,
"paused", "Z");
- GET_FIELD_ID(gInputWindowClassInfo.layer, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.layer, clazz,
"layer", "I");
- GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.ownerPid, clazz,
"ownerPid", "I");
- GET_FIELD_ID(gInputWindowClassInfo.ownerUid, gInputWindowClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowClassInfo.ownerUid, clazz,
"ownerUid", "I");
return 0;
}
diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp
index 5b74e43..aaf679c 100644
--- a/services/jni/com_android_server_InputWindowHandle.cpp
+++ b/services/jni/com_android_server_InputWindowHandle.cpp
@@ -27,8 +27,6 @@
namespace android {
static struct {
- jclass clazz;
-
jfieldID ptr;
jfieldID inputApplicationHandle;
} gInputWindowHandleClassInfo;
@@ -108,8 +106,7 @@ static JNINativeMethod gInputWindowHandleMethods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
@@ -120,13 +117,14 @@ int register_android_server_InputWindowHandle(JNIEnv* env) {
gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
- FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/wm/InputWindowHandle");
+ jclass clazz;
+ FIND_CLASS(clazz, "com/android/server/wm/InputWindowHandle");
- GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, gInputWindowHandleClassInfo.clazz,
+ GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz,
"ptr", "I");
GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle,
- gInputWindowHandleClassInfo.clazz,
+ clazz,
"inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;");
return 0;
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index 705be60..a389c11 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -35,8 +35,6 @@ namespace android {
// ----------------------------------------------------------------------------
static struct {
- jclass clazz;
-
jmethodID goToSleep;
jmethodID userActivity;
} gPowerManagerServiceClassInfo;
@@ -144,8 +142,7 @@ static JNINativeMethod gPowerManagerServiceMethods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
@@ -162,12 +159,13 @@ int register_android_server_PowerManagerService(JNIEnv* env) {
// Callbacks
- FIND_CLASS(gPowerManagerServiceClassInfo.clazz, "com/android/server/PowerManagerService");
+ jclass clazz;
+ FIND_CLASS(clazz, "com/android/server/PowerManagerService");
- GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, gPowerManagerServiceClassInfo.clazz,
+ GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, clazz,
"goToSleep", "(J)V");
- GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz,
+ GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, clazz,
"userActivity", "(JZIZ)V");
// Initialize
diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp
index 6aeede2..9cd04f6 100644
--- a/services/jni/com_android_server_UsbService.cpp
+++ b/services/jni/com_android_server_UsbService.cpp
@@ -37,13 +37,6 @@
namespace android
{
-static struct file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
static struct parcel_file_descriptor_offsets_t
{
jclass mClass;
@@ -167,11 +160,8 @@ static jobject android_server_UsbService_openDevice(JNIEnv *env, jobject thiz, j
int newFD = dup(fd);
usb_device_close(device);
- jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- if (fileDescriptor != NULL) {
- env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, newFD);
- } else {
+ jobject fileDescriptor = jniCreateFileDescriptor(env, newFD);
+ if (fileDescriptor == NULL) {
return NULL;
}
return env->NewObject(gParcelFileDescriptorOffsets.mClass,
@@ -221,11 +211,8 @@ static jobject android_server_UsbService_openAccessory(JNIEnv *env, jobject thiz
LOGE("could not open %s", DRIVER_NAME);
return NULL;
}
- jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- if (fileDescriptor != NULL) {
- env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd);
- } else {
+ jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
+ if (fileDescriptor == NULL) {
return NULL;
}
return env->NewObject(gParcelFileDescriptorOffsets.mClass,
@@ -260,15 +247,7 @@ int register_android_server_UsbService(JNIEnv *env)
return -1;
}
- clazz = env->FindClass("java/io/FileDescriptor");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
- gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
- gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
- LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
- "Unable to find descriptor field in java.io.FileDescriptor");
-
- clazz = env->FindClass("android/os/ParcelFileDescriptor");
+ clazz = env->FindClass("android/os/ParcelFileDescriptor");
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 7e17fdd..c50e4a1 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -27,7 +27,7 @@ LOCAL_SHARED_LIBRARIES := \
libui \
libgui
-LOCAL_PRELINK_MODULE := false
+
LOCAL_MODULE:= libsensorservice
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 8a00a2e..9daaad8 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -41,7 +41,7 @@ LOCAL_SHARED_LIBRARIES := \
libGLESv1_CM \
libbinder \
libui \
- libsurfaceflinger_client
+ libgui
LOCAL_C_INCLUDES := \
$(call include-path-for, corecg graphics)
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 64cff96..a774841 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -93,7 +93,11 @@ int DisplayHardware::getWidth() const { return mWidth; }
int DisplayHardware::getHeight() const { return mHeight; }
PixelFormat DisplayHardware::getFormat() const { return mFormat; }
uint32_t DisplayHardware::getMaxTextureSize() const { return mMaxTextureSize; }
-uint32_t DisplayHardware::getMaxViewportDims() const { return mMaxViewportDims; }
+
+uint32_t DisplayHardware::getMaxViewportDims() const {
+ return mMaxViewportDims[0] < mMaxViewportDims[1] ?
+ mMaxViewportDims[0] : mMaxViewportDims[1];
+}
void DisplayHardware::init(uint32_t dpy)
{
@@ -228,7 +232,7 @@ void DisplayHardware::init(uint32_t dpy)
eglQueryString(display, EGL_EXTENSIONS));
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
#ifdef EGL_ANDROID_swap_rectangle
@@ -260,7 +264,7 @@ void DisplayHardware::init(uint32_t dpy)
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("GL_MAX_VIEWPORT_DIMS = %d x %d", mMaxViewportDims[0], mMaxViewportDims[1]);
LOGI("flags = %08x", mFlags);
// Unbind the context from this thread
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index ee7a2af..cdf89fd 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -108,7 +108,7 @@ private:
PixelFormat mFormat;
uint32_t mFlags;
mutable uint32_t mPageFlipCount;
- GLint mMaxViewportDims;
+ GLint mMaxViewportDims[2];
GLint mMaxTextureSize;
HWComposer* mHwc;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index 90865da..59b7e5a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -38,23 +38,10 @@
#include "SurfaceFlinger.h"
// ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
-// ----------------------------------------------------------------------------
namespace android {
-static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep";
-static char const * kWakeFileName = "/sys/power/wait_for_fb_wake";
-static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep";
-static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake";
-
-// This dir exists if the framebuffer console is present, either built into
-// the kernel or loaded as a module.
-static char const * const kFbconSysDir = "/sys/class/graphics/fbcon";
+static char const * const kSleepFileName = "/sys/power/wait_for_fb_sleep";
+static char const * const kWakeFileName = "/sys/power/wait_for_fb_wake";
// ----------------------------------------------------------------------------
@@ -122,237 +109,13 @@ status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const
status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
{
- if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) {
- if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) {
- LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName);
- return NO_INIT;
- }
- kSleepFileName = kOldSleepFileName;
- kWakeFileName = kOldWakeFileName;
- }
return NO_ERROR;
}
status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
{
- return (((access(kSleepFileName, R_OK) == 0 &&
- access(kWakeFileName, R_OK) == 0) ||
- (access(kOldSleepFileName, R_OK) == 0 &&
- access(kOldWakeFileName, R_OK) == 0)) &&
- access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT;
-}
-
-// ----------------------------------------------------------------------------
-
-pid_t DisplayHardwareBase::ConsoleManagerThread::sSignalCatcherPid = 0;
-
-DisplayHardwareBase::ConsoleManagerThread::ConsoleManagerThread(
- const sp<SurfaceFlinger>& flinger)
- : DisplayEventThreadBase(flinger), consoleFd(-1)
-{
- sSignalCatcherPid = 0;
-
- // create a new console
- char const * const ttydev = "/dev/tty0";
- int fd = open(ttydev, O_RDWR | O_SYNC);
- if (fd<0) {
- LOGE("Can't open %s", ttydev);
- this->consoleFd = -errno;
- return;
- }
-
- // to make sure that we are in text mode
- int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT);
- if (res<0) {
- LOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)",
- fd, res, strerror(errno));
- }
-
- // get the current console
- struct vt_stat vs;
- res = ioctl(fd, VT_GETSTATE, &vs);
- if (res<0) {
- LOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)",
- fd, res, strerror(errno));
- this->consoleFd = -errno;
- return;
- }
-
- // switch to console 7 (which is what X normaly uses)
- int vtnum = 7;
- do {
- res = ioctl(fd, VT_ACTIVATE, (void*)vtnum);
- } while(res < 0 && errno == EINTR);
- if (res<0) {
- LOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for %d",
- fd, errno, strerror(errno), vtnum);
- this->consoleFd = -errno;
- return;
- }
-
- do {
- res = ioctl(fd, VT_WAITACTIVE, (void*)vtnum);
- } while(res < 0 && errno == EINTR);
- if (res<0) {
- LOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for %d",
- fd, res, errno, strerror(errno), vtnum);
- this->consoleFd = -errno;
- return;
- }
-
- // open the new console
- close(fd);
- fd = open(ttydev, O_RDWR | O_SYNC);
- if (fd<0) {
- LOGE("Can't open new console %s", ttydev);
- this->consoleFd = -errno;
- return;
- }
-
- /* disable console line buffer, echo, ... */
- struct termios ttyarg;
- ioctl(fd, TCGETS , &ttyarg);
- ttyarg.c_iflag = 0;
- ttyarg.c_lflag = 0;
- ioctl(fd, TCSETS , &ttyarg);
-
- // set up signals so we're notified when the console changes
- // we can't use SIGUSR1 because it's used by the java-vm
- vm.mode = VT_PROCESS;
- vm.waitv = 0;
- vm.relsig = SIGUSR2;
- vm.acqsig = SIGUNUSED;
- vm.frsig = 0;
-
- struct sigaction act;
- sigemptyset(&act.sa_mask);
- act.sa_handler = sigHandler;
- act.sa_flags = 0;
- sigaction(vm.relsig, &act, NULL);
-
- sigemptyset(&act.sa_mask);
- act.sa_handler = sigHandler;
- act.sa_flags = 0;
- sigaction(vm.acqsig, &act, NULL);
-
- sigset_t mask;
- sigemptyset(&mask);
- sigaddset(&mask, vm.relsig);
- sigaddset(&mask, vm.acqsig);
- sigprocmask(SIG_BLOCK, &mask, NULL);
-
- // switch to graphic mode
- res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS);
- LOGW_IF(res<0,
- "ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res);
-
- this->prev_vt_num = vs.v_active;
- this->vt_num = vtnum;
- this->consoleFd = fd;
-}
-
-DisplayHardwareBase::ConsoleManagerThread::~ConsoleManagerThread()
-{
- if (this->consoleFd >= 0) {
- int fd = this->consoleFd;
- int prev_vt_num = this->prev_vt_num;
- int res;
- ioctl(fd, KDSETMODE, (void*)KD_TEXT);
- do {
- res = ioctl(fd, VT_ACTIVATE, (void*)prev_vt_num);
- } while(res < 0 && errno == EINTR);
- do {
- res = ioctl(fd, VT_WAITACTIVE, (void*)prev_vt_num);
- } while(res < 0 && errno == EINTR);
- close(fd);
- char const * const ttydev = "/dev/tty0";
- fd = open(ttydev, O_RDWR | O_SYNC);
- ioctl(fd, VT_DISALLOCATE, 0);
- close(fd);
- }
-}
-
-status_t DisplayHardwareBase::ConsoleManagerThread::readyToRun()
-{
- if (this->consoleFd >= 0) {
- sSignalCatcherPid = gettid();
-
- sigset_t mask;
- sigemptyset(&mask);
- sigaddset(&mask, vm.relsig);
- sigaddset(&mask, vm.acqsig);
- sigprocmask(SIG_BLOCK, &mask, NULL);
-
- int res = ioctl(this->consoleFd, VT_SETMODE, &vm);
- if (res<0) {
- LOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)",
- this->consoleFd, errno, strerror(errno));
- }
- return NO_ERROR;
- }
- return this->consoleFd;
-}
-
-void DisplayHardwareBase::ConsoleManagerThread::requestExit()
-{
- Thread::requestExit();
- if (sSignalCatcherPid != 0) {
- // wake the thread up
- kill(sSignalCatcherPid, SIGINT);
- // wait for it...
- }
-}
-
-void DisplayHardwareBase::ConsoleManagerThread::sigHandler(int sig)
-{
- // resend the signal to our signal catcher thread
- LOGW("received signal %d in thread %d, resending to %d",
- sig, gettid(), sSignalCatcherPid);
-
- // we absolutely need the delays below because without them
- // our main thread never gets a chance to handle the signal.
- usleep(10000);
- kill(sSignalCatcherPid, sig);
- usleep(10000);
-}
-
-status_t DisplayHardwareBase::ConsoleManagerThread::releaseScreen() const
-{
- int fd = this->consoleFd;
- int err = ioctl(fd, VT_RELDISP, (void*)1);
- LOGE_IF(err<0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)",
- fd, errno, strerror(errno));
- return (err<0) ? (-errno) : status_t(NO_ERROR);
-}
-
-bool DisplayHardwareBase::ConsoleManagerThread::threadLoop()
-{
- sigset_t mask;
- sigemptyset(&mask);
- sigaddset(&mask, vm.relsig);
- sigaddset(&mask, vm.acqsig);
-
- int sig = 0;
- sigwait(&mask, &sig);
-
- if (sig == vm.relsig) {
- sp<SurfaceFlinger> flinger = mFlinger.promote();
- //LOGD("About to give-up screen, flinger = %p", flinger.get());
- if (flinger != 0)
- flinger->screenReleased(0);
- } else if (sig == vm.acqsig) {
- sp<SurfaceFlinger> flinger = mFlinger.promote();
- //LOGD("Screen about to return, flinger = %p", flinger.get());
- if (flinger != 0)
- flinger->screenAcquired(0);
- }
-
- return true;
-}
-
-status_t DisplayHardwareBase::ConsoleManagerThread::initCheck() const
-{
- return consoleFd >= 0 ? NO_ERROR : NO_INIT;
+ return ((access(kSleepFileName, R_OK) == 0 &&
+ access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT;
}
// ----------------------------------------------------------------------------
@@ -362,10 +125,6 @@ DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
: mCanDraw(true), mScreenAcquired(true)
{
mDisplayEventThread = new DisplayEventThread(flinger);
- if (mDisplayEventThread->initCheck() != NO_ERROR) {
- // fall-back on the console
- mDisplayEventThread = new ConsoleManagerThread(flinger);
- }
}
DisplayHardwareBase::~DisplayHardwareBase()
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
index fa6a0c4..30eb258 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
@@ -73,24 +73,6 @@ private:
virtual status_t initCheck() const;
};
- class ConsoleManagerThread : public DisplayEventThreadBase
- {
- int consoleFd;
- int vt_num;
- int prev_vt_num;
- vt_mode vm;
- static void sigHandler(int sig);
- static pid_t sSignalCatcherPid;
- public:
- ConsoleManagerThread(const sp<SurfaceFlinger>& flinger);
- virtual ~ConsoleManagerThread();
- virtual bool threadLoop();
- virtual status_t readyToRun();
- virtual void requestExit();
- virtual status_t releaseScreen() const;
- virtual status_t initCheck() const;
- };
-
sp<DisplayEventThreadBase> mDisplayEventThread;
mutable int mCanDraw;
mutable int mScreenAcquired;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 99d904d..b2f95cd 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -52,6 +52,7 @@ template <typename T> inline T min(T a, T b) {
Layer::Layer(SurfaceFlinger* flinger,
DisplayID display, const sp<Client>& client)
: LayerBaseClient(flinger, display, client),
+ mFormat(PIXEL_FORMAT_NONE),
mGLExtensions(GLExtensions::getInstance()),
mNeedsBlending(true),
mNeedsDithering(false),
@@ -59,7 +60,8 @@ Layer::Layer(SurfaceFlinger* flinger,
mProtectedByApp(false),
mTextureManager(),
mBufferManager(mTextureManager),
- mWidth(0), mHeight(0), mNeedsScaling(false), mFixedSize(false)
+ mWidth(0), mHeight(0),
+ mNeedsScaling(false), mFixedSize(false)
{
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a9fa1ef..e8f0328 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -395,7 +395,7 @@ bool SurfaceFlinger::threadLoop()
if (LIKELY(mTransactionCount == 0)) {
// if we're in a global transaction, don't do anything.
const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
- uint32_t transactionFlags = getTransactionFlags(mask);
+ uint32_t transactionFlags = peekTransactionFlags(mask);
if (LIKELY(transactionFlags)) {
handleTransaction(transactionFlags);
}
@@ -490,7 +490,17 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
Mutex::Autolock _l(mStateLock);
const nsecs_t now = systemTime();
mDebugInTransaction = now;
+
+ // Here we're guaranteed that some transaction flags are set
+ // so we can call handleTransactionLocked() unconditionally.
+ // We call getTransactionFlags(), which will also clear the flags,
+ // with mStateLock held to guarantee that mCurrentState won't change
+ // until the transaction is commited.
+
+ const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
+ transactionFlags = getTransactionFlags(mask);
handleTransactionLocked(transactionFlags, ditchedLayers);
+
mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
invalidateHwcGeometry();
@@ -600,7 +610,7 @@ sp<FreezeLock> SurfaceFlinger::getFreezeLock() const
}
void SurfaceFlinger::computeVisibleRegions(
- LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
+ const LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
{
const GraphicPlane& plane(graphicPlane(0));
const Transform& planeTransform(plane.transform());
@@ -735,8 +745,7 @@ void SurfaceFlinger::commitTransaction()
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
- LayerVector& currentLayers(
- const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
+ const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -748,9 +757,8 @@ void SurfaceFlinger::handlePageFlip()
/*
* rebuild the visible layer list
*/
+ const size_t count = currentLayers.size();
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())
@@ -1096,15 +1104,15 @@ status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
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);
+ size_t name = client->attachLayer(lbc);
+
+ Mutex::Autolock _l(mStateLock);
// add this layer to the current state list
addLayer_l(lbc);
- return name;
+ return ssize_t(name);
}
status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer)
@@ -1155,6 +1163,11 @@ status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
return NO_ERROR;
}
+uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t flags)
+{
+ return android_atomic_release_load(&mTransactionFlags);
+}
+
uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags)
{
return android_atomic_and(~flags, &mTransactionFlags) & flags;
@@ -1243,8 +1256,10 @@ int SurfaceFlinger::setOrientation(DisplayID dpy,
return orientation;
}
-sp<ISurface> SurfaceFlinger::createSurface(const sp<Client>& client, int pid,
- const String8& name, ISurfaceComposerClient::surface_data_t* params,
+sp<ISurface> SurfaceFlinger::createSurface(
+ ISurfaceComposerClient::surface_data_t* params,
+ const String8& name,
+ const sp<Client>& client,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
@@ -2381,15 +2396,17 @@ status_t Client::initCheck() const {
return NO_ERROR;
}
-ssize_t Client::attachLayer(const sp<LayerBaseClient>& layer)
+size_t Client::attachLayer(const sp<LayerBaseClient>& layer)
{
- int32_t name = android_atomic_inc(&mNameGenerator);
+ Mutex::Autolock _l(mLock);
+ size_t name = mNameGenerator++;
mLayers.add(name, layer);
return name;
}
void Client::detachLayer(const LayerBaseClient* layer)
{
+ Mutex::Autolock _l(mLock);
// 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++) {
@@ -2399,9 +2416,11 @@ void Client::detachLayer(const LayerBaseClient* layer)
}
}
}
-sp<LayerBaseClient> Client::getLayerUser(int32_t i) const {
+sp<LayerBaseClient> Client::getLayerUser(int32_t i) const
+{
+ Mutex::Autolock _l(mLock);
sp<LayerBaseClient> lbc;
- const wp<LayerBaseClient>& layer(mLayers.valueFor(i));
+ wp<LayerBaseClient> layer(mLayers.valueFor(i));
if (layer != 0) {
lbc = layer.promote();
LOGE_IF(lbc==0, "getLayerUser(name=%d) is dead", int(i));
@@ -2416,12 +2435,12 @@ ssize_t Client::getTokenForSurface(const sp<ISurface>& sur) const {
return -1;
}
sp<ISurface> Client::createSurface(
- ISurfaceComposerClient::surface_data_t* params, int pid,
+ ISurfaceComposerClient::surface_data_t* params,
const String8& name,
DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
- return mFlinger->createSurface(this, pid, name, params,
+ return mFlinger->createSurface(params, name, this,
display, w, h, format, flags);
}
status_t Client::destroySurface(SurfaceID sid) {
@@ -2515,7 +2534,7 @@ ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const
}
break;
}
- if (++name >= SharedBufferStack::NUM_LAYERS_MAX)
+ if (++name >= int32_t(SharedBufferStack::NUM_LAYERS_MAX))
name = NO_MEMORY;
} while(name >= 0);
@@ -2525,7 +2544,7 @@ ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const
}
sp<ISurface> UserClient::createSurface(
- ISurfaceComposerClient::surface_data_t* params, int pid,
+ ISurfaceComposerClient::surface_data_t* params,
const String8& name,
DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags) {
@@ -2555,22 +2574,9 @@ sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h
LOGE("createGraphicBuffer: unable to create GraphicBuffer");
return 0;
}
- Mutex::Autolock _l(mLock);
- mBuffers.add(graphicBuffer);
return graphicBuffer;
}
-void GraphicBufferAlloc::freeAllGraphicBuffersExcept(int bufIdx) {
- Mutex::Autolock _l(mLock);
- if (0 <= bufIdx && bufIdx < mBuffers.size()) {
- sp<GraphicBuffer> b(mBuffers[bufIdx]);
- mBuffers.clear();
- mBuffers.add(b);
- } else {
- mBuffers.clear();
- }
-}
-
// ---------------------------------------------------------------------------
GraphicPlane::GraphicPlane()
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9566819..1e16943 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -65,7 +65,7 @@ public:
status_t initCheck() const;
// protected by SurfaceFlinger::mStateLock
- ssize_t attachLayer(const sp<LayerBaseClient>& layer);
+ size_t attachLayer(const sp<LayerBaseClient>& layer);
void detachLayer(const LayerBaseClient* layer);
sp<LayerBaseClient> getLayerUser(int32_t i) const;
@@ -75,15 +75,21 @@ private:
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,
+ surface_data_t* params, 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;
+ // constant
sp<SurfaceFlinger> mFlinger;
- int32_t mNameGenerator;
+
+ // protected by mLock
+ DefaultKeyedVector< size_t, wp<LayerBaseClient> > mLayers;
+ size_t mNameGenerator;
+
+ // thread-safe
+ mutable Mutex mLock;
};
class UserClient : public BnSurfaceComposerClient
@@ -107,7 +113,7 @@ private:
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,
+ surface_data_t* params, const String8& name,
DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
uint32_t flags);
virtual status_t destroySurface(SurfaceID surfaceId);
@@ -125,14 +131,8 @@ class GraphicBufferAlloc : public BnGraphicBufferAlloc
public:
GraphicBufferAlloc();
virtual ~GraphicBufferAlloc();
-
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
PixelFormat format, uint32_t usage);
- virtual void freeAllGraphicBuffersExcept(int bufIdx);
-
-private:
- Vector<sp<GraphicBuffer> > mBuffers;
- Mutex mLock;
};
// ---------------------------------------------------------------------------
@@ -238,9 +238,10 @@ private:
friend class Layer;
friend class LayerDim;
- sp<ISurface> createSurface(const sp<Client>& client,
- int pid, const String8& name,
+ sp<ISurface> createSurface(
ISurfaceComposerClient::surface_data_t* params,
+ const String8& name,
+ const sp<Client>& client,
DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags);
@@ -304,7 +305,7 @@ private:
Vector< sp<LayerBase> >& ditchedLayers);
void computeVisibleRegions(
- LayerVector& currentLayers,
+ const LayerVector& currentLayers,
Region& dirtyRegion,
Region& wormholeRegion);
@@ -324,6 +325,7 @@ private:
status_t purgatorizeLayer_l(const sp<LayerBase>& layer);
uint32_t getTransactionFlags(uint32_t flags);
+ uint32_t peekTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags);
void commitTransaction();
@@ -371,7 +373,6 @@ private:
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState;
- State mDrawingState;
volatile int32_t mTransactionFlags;
volatile int32_t mTransactionCount;
Condition mTransactionCV;
@@ -395,6 +396,7 @@ private:
// Can only accessed from the main thread, these members
// don't need synchronization
+ State mDrawingState;
Region mDirtyRegion;
Region mDirtyRegionRemovedLayer;
Region mInvalidRegion;
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
index 9e24f90..bb63c37 100644
--- a/services/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -144,7 +144,7 @@ status_t TextureManager::initEglImage(Image* pImage,
}
// construct an EGL_NATIVE_BUFFER_ANDROID
- android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
+ ANativeWindowBuffer* clientBuf = buffer->getNativeBuffer();
// create the new EGLImageKHR
const EGLint attrs[] = {
diff --git a/services/surfaceflinger/tests/resize/Android.mk b/services/surfaceflinger/tests/resize/Android.mk
index 24c2d01..d81679e 100644
--- a/services/surfaceflinger/tests/resize/Android.mk
+++ b/services/surfaceflinger/tests/resize/Android.mk
@@ -8,7 +8,7 @@ LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libui \
- libsurfaceflinger_client
+ libgui
LOCAL_MODULE:= test-resize
diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
index 0ccca77..18c54b3 100644
--- a/services/surfaceflinger/tests/resize/resize.cpp
+++ b/services/surfaceflinger/tests/resize/resize.cpp
@@ -29,13 +29,6 @@
using namespace android;
namespace android {
-class Test {
-public:
- static const sp<ISurface>& getISurface(const sp<Surface>& s) {
- return s->getISurface();
- }
-};
-};
int main(int argc, char** argv)
{
diff --git a/services/surfaceflinger/tests/screencap/Android.mk b/services/surfaceflinger/tests/screencap/Android.mk
index 1cfb471..5cdd1a8 100644
--- a/services/surfaceflinger/tests/screencap/Android.mk
+++ b/services/surfaceflinger/tests/screencap/Android.mk
@@ -10,7 +10,7 @@ LOCAL_SHARED_LIBRARIES := \
libbinder \
libskia \
libui \
- libsurfaceflinger_client
+ libgui
LOCAL_MODULE:= test-screencap
diff --git a/services/surfaceflinger/tests/surface/Android.mk b/services/surfaceflinger/tests/surface/Android.mk
index ce0e807..c59060e 100644
--- a/services/surfaceflinger/tests/surface/Android.mk
+++ b/services/surfaceflinger/tests/surface/Android.mk
@@ -9,7 +9,7 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder \
libui \
- libsurfaceflinger_client
+ libgui
LOCAL_MODULE:= test-surface
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
index 67ecf7e..5265f91 100644
--- a/services/surfaceflinger/tests/surface/surface.cpp
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -53,7 +53,7 @@ int main(int argc, char** argv)
printf("window=%p\n", window);
int err = native_window_set_buffer_count(window, 8);
- android_native_buffer_t* buffer;
+ ANativeWindowBuffer* buffer;
for (int i=0 ; i<8 ; i++) {
window->dequeueBuffer(window, &buffer);
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 186b349..295f324 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -7,7 +7,9 @@ LOCAL_MODULE_TAGS := tests
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := easymocklib
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ easymocklib \
+ guava
LOCAL_JAVA_LIBRARIES := android.test.runner services
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f115f42..f8d1426 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -15,11 +15,15 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.servicestests">
+ package="com.android.frameworks.servicestests">
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
new file mode 100644
index 0000000..f20d5e5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.AbstractFuture;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.INetworkManagementEventObserver;
+import android.net.NetworkStats;
+import android.net.ThrottleManager;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.Suppress;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.util.TrustedTime;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Future;
+
+/**
+ * Tests for {@link ThrottleService}.
+ */
+public class ThrottleServiceTest extends AndroidTestCase {
+ private static final String TAG = "ThrottleServiceTest";
+
+ private static final long MB_IN_BYTES = 1024 * 1024;
+
+ private static final int TEST_KBITPS = 222;
+ private static final int TEST_RESET_DAY = 11;
+
+ private static final String TEST_IFACE = "test0";
+
+ private WatchingContext mWatchingContext;
+ private INetworkManagementService mMockNMService;
+ private TrustedTime mMockTime;
+
+ private ThrottleService mThrottleService;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mWatchingContext = new WatchingContext(getContext());
+
+ mMockNMService = createMock(INetworkManagementService.class);
+ mMockTime = createMock(TrustedTime.class);
+
+ mThrottleService = new ThrottleService(
+ mWatchingContext, mMockNMService, mMockTime, TEST_IFACE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mWatchingContext = null;
+ mMockNMService = null;
+
+ mThrottleService.shutdown();
+ mThrottleService = null;
+
+ clearThrottlePolicy();
+
+ super.tearDown();
+ }
+
+ public void testNoPolicyNotThrottled() throws Exception {
+ expectTimeCurrent();
+ expectSystemReady();
+
+ // provide stats without policy, verify not throttled
+ expectGetInterfaceCounter(1 * MB_IN_BYTES, 2 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(-1, -1);
+
+ replay(mMockTime, mMockNMService);
+ systemReady();
+ verify(mMockTime, mMockNMService);
+ }
+
+ public void testUnderLimitNotThrottled() throws Exception {
+ setThrottlePolicy(200 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
+
+ expectTimeCurrent();
+ expectSystemReady();
+
+ // provide stats under limits, and verify not throttled
+ expectGetInterfaceCounter(1 * MB_IN_BYTES, 2 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(-1, -1);
+
+ replay(mMockTime, mMockNMService);
+ systemReady();
+ verify(mMockTime, mMockNMService);
+ }
+
+ public void testOverLimitThrottled() throws Exception {
+ setThrottlePolicy(200 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
+
+ expectTimeCurrent();
+ expectSystemReady();
+
+ // provide stats over limits, and verify throttled
+ expectGetInterfaceCounter(500 * MB_IN_BYTES, 600 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(TEST_KBITPS, TEST_KBITPS);
+
+ replay(mMockTime, mMockNMService);
+ systemReady();
+ verify(mMockTime, mMockNMService);
+ }
+
+ public void testUnderThenOverLimitThrottled() throws Exception {
+ setThrottlePolicy(201 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
+
+ expectTimeCurrent();
+ expectSystemReady();
+
+ // provide stats right under 201MB limit, verify not throttled
+ expectGetInterfaceCounter(100 * MB_IN_BYTES, 100 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(-1, -1);
+
+ replay(mMockTime, mMockNMService);
+ systemReady();
+ verify(mMockTime, mMockNMService);
+ reset(mMockTime, mMockNMService);
+
+ expectTimeCurrent();
+
+ // adjust usage to bump over limit, verify throttle kicks in
+ expectGetInterfaceCounter(105 * MB_IN_BYTES, 100 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(TEST_KBITPS, TEST_KBITPS);
+
+ // and kick poll event which should throttle
+ replay(mMockTime, mMockNMService);
+ forceServicePoll();
+ verify(mMockTime, mMockNMService);
+ }
+
+ public void testUpdatedPolicyThrottled() throws Exception {
+ setThrottlePolicy(500 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
+
+ expectTimeCurrent();
+ expectSystemReady();
+
+ // provide stats under limit, verify not throttled
+ expectGetInterfaceCounter(50 * MB_IN_BYTES, 50 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(-1, -1);
+
+ replay(mMockTime, mMockNMService);
+ systemReady();
+ verify(mMockTime, mMockNMService);
+ reset(mMockTime, mMockNMService);
+
+ expectTimeCurrent();
+
+ // provide same stats, but verify that modified policy will throttle
+ expectGetInterfaceCounter(50 * MB_IN_BYTES, 50 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(TEST_KBITPS, TEST_KBITPS);
+
+ replay(mMockTime, mMockNMService);
+
+ // now adjust policy to bump usage over limit
+ setThrottlePolicy(5 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
+
+ // and wait for policy updated broadcast
+ mWatchingContext.nextBroadcastIntent(ThrottleManager.POLICY_CHANGED_ACTION).get();
+
+ verify(mMockTime, mMockNMService);
+ }
+
+ public void testWithPolicyOverLimitThrottledAndRemovedAfterCycle() throws Exception {
+ setThrottlePolicy(90 * MB_IN_BYTES, TEST_KBITPS, TEST_RESET_DAY);
+
+ final long baseTime = System.currentTimeMillis();
+
+ expectTime(baseTime);
+ expectSystemReady();
+
+ // provide stats over limit, verify throttle kicks in
+ expectGetInterfaceCounter(50 * MB_IN_BYTES, 50 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(TEST_KBITPS, TEST_KBITPS);
+
+ replay(mMockTime, mMockNMService);
+ systemReady();
+ verify(mMockTime, mMockNMService);
+ reset(mMockTime, mMockNMService);
+
+ // pretend that time has jumped forward two months
+ expectTime(baseTime + DateUtils.WEEK_IN_MILLIS * 8);
+
+ // provide slightly updated stats, but verify throttle is removed
+ expectGetInterfaceCounter(60 * MB_IN_BYTES, 60 * MB_IN_BYTES);
+ expectSetInterfaceThrottle(-1, -1);
+
+ // and kick poll event which should throttle
+ replay(mMockTime, mMockNMService);
+ forceServiceReset();
+ verify(mMockTime, mMockNMService);
+ }
+
+ @Suppress
+ public void testReturnStats() throws Exception {
+ final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ final INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
+
+ // test is currently no-op, just exercises stats apis
+ Log.d(TAG, nmService.getNetworkStatsSummary().toString());
+ Log.d(TAG, nmService.getNetworkStatsDetail().toString());
+ }
+
+ /**
+ * Persist the given {@link ThrottleService} policy into {@link Settings}.
+ */
+ public void setThrottlePolicy(long thresholdBytes, int valueKbitps, int resetDay) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ Settings.Secure.putLong(resolver, Settings.Secure.THROTTLE_THRESHOLD_BYTES, thresholdBytes);
+ Settings.Secure.putInt(resolver, Settings.Secure.THROTTLE_VALUE_KBITSPS, valueKbitps);
+ Settings.Secure.putInt(resolver, Settings.Secure.THROTTLE_RESET_DAY, resetDay);
+ }
+
+ /**
+ * Clear any {@link ThrottleService} policy from {@link Settings}.
+ */
+ public void clearThrottlePolicy() {
+ final ContentResolver resolver = getContext().getContentResolver();
+ Settings.Secure.putString(resolver, Settings.Secure.THROTTLE_THRESHOLD_BYTES, null);
+ Settings.Secure.putString(resolver, Settings.Secure.THROTTLE_VALUE_KBITSPS, null);
+ Settings.Secure.putString(resolver, Settings.Secure.THROTTLE_RESET_DAY, null);
+ }
+
+ /**
+ * Expect any {@link TrustedTime} mock calls, and respond with
+ * {@link System#currentTimeMillis()}.
+ */
+ public void expectTimeCurrent() throws Exception {
+ expectTime(System.currentTimeMillis());
+ }
+
+ /**
+ * Expect any {@link TrustedTime} mock calls, and respond with the given
+ * time in response to {@link TrustedTime#currentTimeMillis()}.
+ */
+ public void expectTime(long currentTime) throws Exception {
+ expect(mMockTime.forceRefresh()).andReturn(false).anyTimes();
+ expect(mMockTime.hasCache()).andReturn(true).anyTimes();
+ expect(mMockTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
+ expect(mMockTime.getCacheAge()).andReturn(0L).anyTimes();
+ expect(mMockTime.getCacheCertainty()).andReturn(0L).anyTimes();
+ }
+
+ /**
+ * Expect {@link ThrottleService#systemReady()} generated calls, such as
+ * connecting with {@link NetworkManagementService} mock.
+ */
+ public void expectSystemReady() throws Exception {
+ mMockNMService.registerObserver(isA(INetworkManagementEventObserver.class));
+ expectLastCall().atLeastOnce();
+ }
+
+ /**
+ * Expect {@link NetworkManagementService#getNetworkStatsSummary()} mock
+ * calls, responding with the given counter values.
+ */
+ public void expectGetInterfaceCounter(long rx, long tx) throws Exception {
+ // TODO: provide elapsedRealtime mock to match TimeAuthority
+ final NetworkStats.Builder stats = new NetworkStats.Builder(
+ SystemClock.elapsedRealtime(), 1);
+ stats.addEntry(TEST_IFACE, NetworkStats.UID_ALL, rx, tx);
+
+ expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats.build()).atLeastOnce();
+ }
+
+ /**
+ * Expect {@link NetworkManagementService#setInterfaceThrottle} mock call
+ * with the specified parameters.
+ */
+ public void expectSetInterfaceThrottle(int rx, int tx) throws Exception {
+ mMockNMService.setInterfaceThrottle(isA(String.class), eq(rx), eq(tx));
+ expectLastCall().atLeastOnce();
+ }
+
+ /**
+ * Dispatch {@link ThrottleService#systemReady()} and block until finished.
+ */
+ public void systemReady() throws Exception {
+ final Future<Intent> policyChanged = mWatchingContext.nextBroadcastIntent(
+ ThrottleManager.POLICY_CHANGED_ACTION);
+ final Future<Intent> pollAction = mWatchingContext.nextBroadcastIntent(
+ ThrottleManager.THROTTLE_POLL_ACTION);
+
+ mThrottleService.systemReady();
+
+ // wait for everything to settle; for policy to update and for first poll
+ policyChanged.get();
+ pollAction.get();
+ }
+
+ /**
+ * Dispatch {@link ThrottleService#dispatchPoll()} and block until finished.
+ */
+ public void forceServicePoll() throws Exception {
+ // during systemReady() service already pushed a sticky broadcast, so we
+ // need to skip the immediate and wait for the updated sticky.
+ final Future<Intent> pollAction = mWatchingContext.nextBroadcastIntent(
+ ThrottleManager.THROTTLE_POLL_ACTION);
+
+ mThrottleService.dispatchPoll();
+
+ pollAction.get();
+ }
+
+ /**
+ * Dispatch {@link ThrottleService#dispatchReset()} and block until finished.
+ */
+ public void forceServiceReset() throws Exception {
+ // during systemReady() service already pushed a sticky broadcast, so we
+ // need to skip the immediate and wait for the updated sticky.
+ final Future<Intent> pollAction = mWatchingContext.nextBroadcastIntent(
+ ThrottleManager.THROTTLE_POLL_ACTION);
+
+ mThrottleService.dispatchReset();
+
+ pollAction.get();
+ }
+
+
+ /**
+ * {@link ContextWrapper} that can attach listeners for upcoming
+ * {@link Context#sendBroadcast(Intent)}.
+ */
+ private static class WatchingContext extends ContextWrapper {
+ private List<LocalBroadcastReceiver> mReceivers = Lists.newArrayList();
+
+ public class LocalBroadcastReceiver extends AbstractFuture<Intent> {
+ private IntentFilter mFilter;
+
+ public LocalBroadcastReceiver(IntentFilter filter) {
+ mFilter = filter;
+ }
+
+ public boolean dispatchBroadcast(Intent intent) {
+ if (mFilter.match(getContentResolver(), intent, false, TAG) > 0) {
+ set(intent);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public WatchingContext(Context base) {
+ super(base);
+ }
+
+ public Future<Intent> nextBroadcastIntent(String action) {
+ return nextBroadcastIntent(new IntentFilter(action));
+ }
+
+ public Future<Intent> nextBroadcastIntent(IntentFilter filter) {
+ final LocalBroadcastReceiver receiver = new LocalBroadcastReceiver(filter);
+ synchronized (mReceivers) {
+ mReceivers.add(receiver);
+ }
+ return receiver;
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent) {
+ synchronized (mReceivers) {
+ final Iterator<LocalBroadcastReceiver> i = mReceivers.iterator();
+ while (i.hasNext()) {
+ final LocalBroadcastReceiver receiver = i.next();
+ if (receiver.dispatchBroadcast(intent)) {
+ i.remove();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void sendStickyBroadcast(Intent intent) {
+ sendBroadcast(intent);
+ }
+
+ @Override
+ public void removeStickyBroadcast(Intent intent) {
+ // ignored
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java b/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java
index 71e8e2a..60677df 100755
--- a/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java
@@ -15,17 +15,25 @@
*/
package com.android.server.location;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Timer;
-
import android.location.Country;
import android.location.CountryListener;
import android.location.Location;
import android.location.LocationListener;
+import android.location.LocationManager;
import android.test.AndroidTestCase;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Timer;
+
public class LocationBasedCountryDetectorTest extends AndroidTestCase {
+ private static final List<String> sEnabledProviders = Arrays.asList(
+ LocationManager.GPS_PROVIDER, LocationManager.PASSIVE_PROVIDER);
private class TestCountryDetector extends LocationBasedCountryDetector {
public static final int TOTAL_PROVIDERS = 2;
protected Object countryFoundLocker = new Object();
@@ -33,7 +41,7 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
private final Location mLocation;
private final String mCountry;
private final long mQueryLocationTimeout;
- private List<LocationListener> mListeners;
+ private Map<String, LocationListener> mListeners;
public TestCountryDetector(String country, String provider) {
this(country, provider, 1000 * 60 * 5);
@@ -44,7 +52,7 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
mCountry = country;
mLocation = new Location(provider);
mQueryLocationTimeout = queryLocationTimeout;
- mListeners = new ArrayList<LocationListener>();
+ mListeners = new HashMap<String, LocationListener>();
}
@Override
@@ -69,16 +77,40 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
return mLocation;
}
+ private Set<String> mAcceptableProviders;
+
+ public void setAcceptableProvider(Set<String> acceptableProviders) {
+ mAcceptableProviders = acceptableProviders;
+ }
+
+ @Override
+ protected boolean isAcceptableProvider(String provider) {
+ if (mAcceptableProviders != null) {
+ return mAcceptableProviders.contains(provider);
+ } else {
+ return true;
+ }
+ }
+
@Override
- protected void registerEnabledProviders(List<LocationListener> listeners) {
- mListeners.addAll(listeners);
+ protected void registerListener(String provider, LocationListener listener) {
+ assertNotNull(provider);
+ mListeners.put(provider, listener);
}
@Override
- protected void unregisterProviders(List<LocationListener> listeners) {
- for (LocationListener listener : mLocationListeners) {
- assertTrue(mListeners.remove(listener));
+ protected void unregisterListener(LocationListener listener) {
+ for (Entry<String, LocationListener> entry : mListeners.entrySet()) {
+ if (entry.getValue().equals(listener)) {
+ mListeners.remove(entry.getKey());
+ return;
+ }
}
+ fail("Not registered");
+ }
+
+ public Map<String, LocationListener> getListeners() {
+ return mListeners;
}
@Override
@@ -87,8 +119,8 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
}
@Override
- protected int getTotalEnabledProviders() {
- return TOTAL_PROVIDERS;
+ protected List<String> getEnabledProviders() {
+ return sEnabledProviders;
}
public void notifyLocationFound() {
@@ -140,16 +172,39 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
}
public void testFindingCountry() {
+ testFindingCountryCommon(null);
+ }
+
+ public void testFindingCountryWithAcceptableProvider() {
+ testFindingCountryCommon(new HashSet<String>(Arrays.asList("passive")));
+ }
+
+ private void testFindingCountryCommon(Set<String> acceptableProviders) {
final String country = "us";
final String provider = "Good";
CountryListenerImpl countryListener = new CountryListenerImpl();
TestCountryDetector detector = new TestCountryDetector(country, provider);
+
+ if (acceptableProviders != null) {
+ detector.setAcceptableProvider(acceptableProviders);
+ }
+
detector.setCountryListener(countryListener);
detector.detectCountry();
- assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+
+ if (acceptableProviders != null) {
+ assertEquals(acceptableProviders.size(), detector.getListenersCount());
+ Map<String, LocationListener> listeners = detector.getListeners();
+ for (String acceptableProvider : acceptableProviders) {
+ assertTrue(listeners.containsKey(acceptableProvider));
+ }
+ } else {
+ assertEquals(TestCountryDetector.TOTAL_PROVIDERS, detector.getListenersCount());
+ }
+
detector.notifyLocationFound();
// All listeners should be unregistered
- assertEquals(detector.getListenersCount(), 0);
+ assertEquals(0, detector.getListenersCount());
assertNull(detector.getTimer());
Thread queryThread = waitForQueryThreadLaunched(detector);
detector.notifyCountryFound();
@@ -168,10 +223,10 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
TestCountryDetector detector = new TestCountryDetector(country, provider);
detector.setCountryListener(countryListener);
detector.detectCountry();
- assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ assertEquals(TestCountryDetector.TOTAL_PROVIDERS, detector.getListenersCount());
detector.notifyLocationFound();
// All listeners should be unregistered
- assertEquals(detector.getListenersCount(), 0);
+ assertEquals(0, detector.getListenersCount());
// The time should be stopped
assertNull(detector.getTimer());
Thread queryThread = waitForQueryThreadLaunched(detector);
@@ -193,10 +248,10 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
TestCountryDetector detector = new TestCountryDetector(country, provider);
detector.setCountryListener(countryListener);
detector.detectCountry();
- assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ assertEquals(TestCountryDetector.TOTAL_PROVIDERS, detector.getListenersCount());
detector.stop();
// All listeners should be unregistered
- assertEquals(detector.getListenersCount(), 0);
+ assertEquals(0, detector.getListenersCount());
// The time should be stopped
assertNull(detector.getTimer());
// QueryThread should still be NULL
@@ -217,10 +272,10 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
CountryListenerImpl countryListener = new CountryListenerImpl();
detector.setCountryListener(countryListener);
detector.detectCountry();
- assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ assertEquals(TestCountryDetector.TOTAL_PROVIDERS, detector.getListenersCount());
waitForTimerReset(detector);
// All listeners should be unregistered
- assertEquals(detector.getListenersCount(), 0);
+ assertEquals(0, detector.getListenersCount());
// QueryThread should still be NULL
assertNull(detector.getQueryThread());
assertTrue(countryListener.notified());
@@ -248,10 +303,10 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
CountryListenerImpl countryListener = new CountryListenerImpl();
detector.setCountryListener(countryListener);
detector.detectCountry();
- assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ assertEquals(TestCountryDetector.TOTAL_PROVIDERS, detector.getListenersCount());
detector.notifyLocationFound();
// All listeners should be unregistered
- assertEquals(detector.getListenersCount(), 0);
+ assertEquals(0, detector.getListenersCount());
assertNull(detector.getTimer());
Thread queryThread = waitForQueryThreadLaunched(detector);
detector.notifyCountryFound();
@@ -272,10 +327,10 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
CountryListenerImpl countryListener = new CountryListenerImpl();
detector.setCountryListener(countryListener);
detector.detectCountry();
- assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+ assertEquals(TestCountryDetector.TOTAL_PROVIDERS, detector.getListenersCount());
waitForTimerReset(detector);
// All listeners should be unregistered
- assertEquals(detector.getListenersCount(), 0);
+ assertEquals(0, detector.getListenersCount());
Thread queryThread = waitForQueryThreadLaunched(detector);
detector.notifyCountryFound();
// Wait for query thread ending
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
new file mode 100644
index 0000000..e8188e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.server.pm.UserManager;
+
+import android.content.pm.UserInfo;
+import android.os.Debug;
+import android.os.Environment;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+
+/** Test {@link UserManager} functionality. */
+public class UserManagerTest extends AndroidTestCase {
+
+ UserManager mUserManager = null;
+
+ @Override
+ public void setUp() throws Exception {
+ mUserManager = new UserManager(Environment.getExternalStorageDirectory(),
+ Environment.getExternalStorageDirectory());
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ List<UserInfo> users = mUserManager.getUsers();
+ // Remove all except the primary user
+ for (UserInfo user : users) {
+ if (!user.isPrimary()) {
+ mUserManager.removeUser(user.id);
+ }
+ }
+ }
+
+ public void testHasPrimary() throws Exception {
+ assertTrue(findUser(0));
+ }
+
+ public void testAddUser() throws Exception {
+ final UserManager details = mUserManager;
+
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+ assertTrue(userInfo != null);
+
+ List<UserInfo> list = details.getUsers();
+ boolean found = false;
+ for (UserInfo user : list) {
+ if (user.id == userInfo.id && user.name.equals("Guest 1")
+ && user.isGuest()
+ && !user.isAdmin()
+ && !user.isPrimary()) {
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+
+ public void testAdd2Users() throws Exception {
+ final UserManager details = mUserManager;
+
+ UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+ UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN, null);
+
+ assertTrue(user1 != null);
+ assertTrue(user2 != null);
+
+ assertTrue(findUser(0));
+ assertTrue(findUser(user1.id));
+ assertTrue(findUser(user2.id));
+ }
+
+ public void testRemoveUser() throws Exception {
+ final UserManager details = mUserManager;
+
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+
+ details.removeUser(userInfo.id);
+
+ assertFalse(findUser(userInfo.id));
+ }
+
+ private boolean findUser(int id) {
+ List<UserInfo> list = mUserManager.getUsers();
+
+ for (UserInfo user : list) {
+ if (user.id == id) {
+ return true;
+ }
+ }
+ return false;
+ }
+}