summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/Android.mk3
-rw-r--r--services/audioflinger/AudioFlinger.cpp2
-rw-r--r--services/audioflinger/AudioResamplerCubic.cpp4
-rw-r--r--services/camera/libcameraservice/CameraHardwareInterface.h18
-rw-r--r--services/camera/libcameraservice/CameraHardwareStub.h9
-rw-r--r--services/camera/libcameraservice/CameraService.cpp9
-rw-r--r--services/input/EventHub.cpp31
-rw-r--r--services/input/EventHub.h4
-rw-r--r--services/input/InputDispatcher.cpp459
-rw-r--r--services/input/InputDispatcher.h24
-rw-r--r--services/input/InputReader.cpp511
-rw-r--r--services/input/InputReader.h53
-rw-r--r--services/input/SpriteController.cpp11
-rw-r--r--services/input/tests/InputReader_test.cpp72
-rw-r--r--services/java/com/android/server/AppWidgetService.java2
-rw-r--r--services/java/com/android/server/BackupManagerService.java181
-rw-r--r--services/java/com/android/server/ConnectivityService.java98
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java90
-rw-r--r--services/java/com/android/server/IntentResolver.java23
-rw-r--r--services/java/com/android/server/LoadAverageService.java4
-rw-r--r--services/java/com/android/server/LocationManagerService.java77
-rw-r--r--services/java/com/android/server/MountService.java32
-rw-r--r--services/java/com/android/server/NetworkManagementService.java315
-rw-r--r--services/java/com/android/server/NetworkTimeUpdateService.java67
-rw-r--r--services/java/com/android/server/SystemBackupAgent.java2
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/java/com/android/server/ThrottleService.java40
-rw-r--r--services/java/com/android/server/WifiService.java9
-rw-r--r--services/java/com/android/server/WifiWatchdogService.java1450
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java108
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java34
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java273
-rw-r--r--services/java/com/android/server/am/ActivityStack.java1
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java13
-rw-r--r--services/java/com/android/server/am/BroadcastFilter.java4
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java10
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java19
-rw-r--r--services/java/com/android/server/connectivity/Vpn.java496
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java59
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java253
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java51
-rw-r--r--services/java/com/android/server/pm/Installer.java7
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java559
-rw-r--r--services/java/com/android/server/usb/UsbDeviceManager.java309
-rw-r--r--services/java/com/android/server/usb/UsbHostManager.java5
-rw-r--r--services/java/com/android/server/usb/UsbSettingsManager.java33
-rw-r--r--services/java/com/android/server/wm/InputManager.java12
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java11
-rw-r--r--services/jni/com_android_server_InputManager.cpp20
-rw-r--r--services/jni/com_android_server_connectivity_Vpn.cpp127
-rwxr-xr-xservices/jni/com_android_server_location_GpsLocationProvider.cpp10
-rw-r--r--services/sensorservice/SensorService.cpp8
-rw-r--r--services/sensorservice/SensorService.h2
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.cpp50
-rw-r--r--services/surfaceflinger/Layer.cpp19
-rw-r--r--services/surfaceflinger/LayerBase.cpp7
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp240
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h33
-rw-r--r--services/surfaceflinger/tests/resize/resize.cpp8
-rw-r--r--services/surfaceflinger/tests/surface/surface.cpp4
-rw-r--r--services/tests/servicestests/res/raw/xt_qtaguid_extended3
-rw-r--r--services/tests/servicestests/res/raw/xt_qtaguid_typical32
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java121
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java181
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java1
65 files changed, 3185 insertions, 3547 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 2222e8b..75d24a1 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -10,6 +10,9 @@ LOCAL_SRC_FILES:= \
AudioResamplerCubic.cpp.arm \
AudioPolicyService.cpp
+LOCAL_C_INCLUDES := \
+ system/media/audio_effects/include
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f716e63..daf94f2 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -50,7 +50,7 @@
#include "AudioFlinger.h"
#include <media/EffectsFactoryApi.h>
-#include <media/EffectVisualizerApi.h>
+#include <audio_effects/effect_visualizer.h>
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp
index 1d247bd..4d721f6 100644
--- a/services/audioflinger/AudioResamplerCubic.cpp
+++ b/services/audioflinger/AudioResamplerCubic.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "AudioSRC"
+
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
@@ -22,8 +24,6 @@
#include "AudioResampler.h"
#include "AudioResamplerCubic.h"
-#define LOG_TAG "AudioSRC"
-
namespace android {
// ----------------------------------------------------------------------------
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
index a3749cf..a583aad 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -559,13 +559,8 @@ private:
ANativeWindowBuffer* anb;
rc = a->dequeueBuffer(a, &anb);
if (!rc) {
- rc = a->lockBuffer(a, anb);
- if (!rc) {
- *buffer = &anb->handle;
- *stride = anb->stride;
- }
- else
- a->cancelBuffer(a, anb);
+ *buffer = &anb->handle;
+ *stride = anb->stride;
}
return rc;
}
@@ -576,6 +571,14 @@ private:
(type *) ((char *) __mptr - (char *)(&((type *)0)->member)); })
#endif
+ static int __lock_buffer(struct preview_stream_ops* w,
+ buffer_handle_t* buffer)
+ {
+ ANativeWindow *a = anw(w);
+ return a->lockBuffer(a,
+ container_of(buffer, ANativeWindowBuffer, handle));
+ }
+
static int __enqueue_buffer(struct preview_stream_ops* w,
buffer_handle_t* buffer)
{
@@ -641,6 +644,7 @@ private:
void initHalPreviewWindow()
{
mHalPreviewWindow.nw.cancel_buffer = __cancel_buffer;
+ mHalPreviewWindow.nw.lock_buffer = __lock_buffer;
mHalPreviewWindow.nw.dequeue_buffer = __dequeue_buffer;
mHalPreviewWindow.nw.enqueue_buffer = __enqueue_buffer;
mHalPreviewWindow.nw.set_buffer_count = __set_buffer_count;
diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h
index 9b66a76..c6d8756 100644
--- a/services/camera/libcameraservice/CameraHardwareStub.h
+++ b/services/camera/libcameraservice/CameraHardwareStub.h
@@ -73,14 +73,7 @@ private:
CameraHardwareStub* mHardware;
public:
PreviewThread(CameraHardwareStub* hw) :
-#ifdef SINGLE_PROCESS
- // In single process mode this thread needs to be a java thread,
- // since we won't be calling through the binder.
- Thread(true),
-#else
- Thread(false),
-#endif
- mHardware(hw) { }
+ Thread(false), mHardware(hw) { }
virtual void onFirstRef() {
run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY);
}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index a011ae2..9b09983 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -420,6 +420,10 @@ status_t CameraService::Client::unlock() {
// allow anyone to use camera (after they lock the camera)
status_t result = checkPid();
if (result == NO_ERROR) {
+ if (mHardware->recordingEnabled()) {
+ LOGE("Not allowed to unlock camera during recording.");
+ return INVALID_OPERATION;
+ }
mClientPid = 0;
LOG1("clear mCameraClient (pid %d)", callingPid);
// we need to remove the reference to ICameraClient so that when the app
@@ -756,6 +760,11 @@ status_t CameraService::Client::takePicture(int msgType) {
status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
+ if (mHardware->recordingEnabled()) {
+ LOGE("Cannot take picture during recording.");
+ return INVALID_OPERATION;
+ }
+
if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
(msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
LOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 95b8a57..ca2540b 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -212,8 +212,8 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
struct input_absinfo info;
if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
- LOGW("Error reading absolute controller %d for device %s fd %d\n",
- axis, device->identifier.name.string(), device->fd);
+ LOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
+ axis, device->identifier.name.string(), device->fd, errno);
return -errno;
}
@@ -335,6 +335,33 @@ int32_t EventHub::getSwitchStateLocked(Device* device, int32_t sw) const {
return AKEY_STATE_UNKNOWN;
}
+status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const {
+ if (axis >= 0 && axis <= ABS_MAX) {
+ AutoMutex _l(mLock);
+
+ Device* device = getDeviceLocked(deviceId);
+ if (device != NULL) {
+ return getAbsoluteAxisValueLocked(device, axis, outValue);
+ }
+ }
+ *outValue = 0;
+ return -1;
+}
+
+status_t EventHub::getAbsoluteAxisValueLocked(Device* device, int32_t axis,
+ int32_t* outValue) const {
+ struct input_absinfo info;
+
+ if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
+ LOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
+ axis, device->identifier.name.string(), device->fd, errno);
+ return -errno;
+ }
+
+ *outValue = info.value;
+ return OK;
+}
+
bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) const {
AutoMutex _l(mLock);
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 0a34e45..695dfdf 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -186,6 +186,8 @@ public:
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
+ virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const = 0;
/*
* Examine key input devices for specific framework keycode support
@@ -237,6 +239,7 @@ public:
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
+ virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const;
virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) const;
@@ -305,6 +308,7 @@ private:
int32_t getScanCodeStateLocked(Device* device, int32_t scanCode) const;
int32_t getKeyCodeStateLocked(Device* device, int32_t keyCode) const;
int32_t getSwitchStateLocked(Device* device, int32_t sw) const;
+ int32_t getAbsoluteAxisValueLocked(Device* device, int32_t axis, int32_t* outValue) const;
bool markSupportedKeyCodesLocked(Device* device, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) const;
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 4a50d8a..10b9083 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -569,9 +569,9 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR
break;
case DROP_REASON_BLOCKED:
LOGI("Dropped event because the current application is not responding and the user "
- "has started interating with a different application.");
+ "has started interacting with a different application.");
reason = "inbound event was dropped because the current application is not responding "
- "and the user has started interating with a different application";
+ "and the user has started interacting with a different application";
break;
case DROP_REASON_STALE:
LOGI("Dropped event because it is stale.");
@@ -1239,7 +1239,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const InputWindow* newHoverWindow = NULL;
bool isSplit = mTouchState.split;
- bool wrongDevice = mTouchState.down
+ bool switchedDevice = mTouchState.deviceId >= 0
&& (mTouchState.deviceId != entry->deviceId
|| mTouchState.source != entry->source);
bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
@@ -1248,28 +1248,27 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN
|| maskedAction == AMOTION_EVENT_ACTION_SCROLL
|| isHoverAction);
+ bool wrongDevice = false;
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- if (wrongDevice && !down) {
+ if (switchedDevice && mTouchState.down && !down) {
+#if DEBUG_FOCUS
+ LOGD("Dropping event because a pointer for a different device is already down.");
+#endif
mTempTouchState.copyFrom(mTouchState);
- } else {
- mTempTouchState.reset();
- mTempTouchState.down = down;
- mTempTouchState.deviceId = entry->deviceId;
- mTempTouchState.source = entry->source;
- isSplit = false;
- wrongDevice = false;
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ switchedDevice = false;
+ wrongDevice = true;
+ goto Failed;
}
+ mTempTouchState.reset();
+ mTempTouchState.down = down;
+ mTempTouchState.deviceId = entry->deviceId;
+ mTempTouchState.source = entry->source;
+ isSplit = false;
} else {
mTempTouchState.copyFrom(mTouchState);
}
- if (wrongDevice) {
-#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 (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
@@ -1598,18 +1597,38 @@ Failed:
// Update final pieces of touch state if the injector had permission.
if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
if (!wrongDevice) {
- if (maskedAction == AMOTION_EVENT_ACTION_UP
- || maskedAction == AMOTION_EVENT_ACTION_CANCEL
- || isHoverAction) {
+ if (switchedDevice) {
+#if DEBUG_FOCUS
+ LOGD("Conflicting pointer actions: Switched to a different device.");
+#endif
+ *outConflictingPointerActions = true;
+ }
+
+ if (isHoverAction) {
+ // Started hovering, therefore no longer down.
+ if (mTouchState.down) {
+#if DEBUG_FOCUS
+ LOGD("Conflicting pointer actions: Hover received while pointer was down.");
+#endif
+ *outConflictingPointerActions = true;
+ }
+ mTouchState.reset();
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
+ || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ mTouchState.deviceId = entry->deviceId;
+ mTouchState.source = entry->source;
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP
+ || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
mTouchState.reset();
} else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// First pointer went down.
if (mTouchState.down) {
- *outConflictingPointerActions = true;
#if DEBUG_FOCUS
- LOGD("Pointer down received while already down.");
+ LOGD("Conflicting pointer actions: Down received while already down.");
#endif
+ *outConflictingPointerActions = true;
}
mTouchState.copyFrom(mTempTouchState);
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
@@ -1868,6 +1887,18 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
return;
}
+ // If the motion event was modified in flight, then we cannot stream the sample.
+ if ((motionEventDispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_MASK)
+ != InputTarget::FLAG_DISPATCH_AS_IS) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Not streaming because the motion event was not "
+ "being dispatched as-is. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName());
+#endif
+ return;
+ }
+
// The dispatch entry is in progress and is still potentially open for streaming.
// Try to stream the new motion sample. This might fail if the consumer has already
// consumed the motion event (or if the channel is broken).
@@ -1972,6 +2003,66 @@ void InputDispatcher::enqueueDispatchEntryLocked(
dispatchEntry->headMotionSample = appendedMotionSample;
}
+ // Apply target flags and update the connection's input state.
+ switch (eventEntry->type) {
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+ dispatchEntry->resolvedAction = keyEntry->action;
+ dispatchEntry->resolvedFlags = keyEntry->flags;
+
+ if (!connection->inputState.trackKey(keyEntry,
+ dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
+ connection->getInputChannelName());
+#endif
+ return; // skip the inconsistent event
+ }
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+ if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
+ } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
+ } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
+ } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
+ } else {
+ dispatchEntry->resolvedAction = motionEntry->action;
+ }
+ if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
+ && !connection->inputState.isHovering(
+ motionEntry->deviceId, motionEntry->source)) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event",
+ connection->getInputChannelName());
+#endif
+ dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ }
+
+ dispatchEntry->resolvedFlags = motionEntry->flags;
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
+ dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ }
+
+ if (!connection->inputState.trackMotion(motionEntry,
+ dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event",
+ connection->getInputChannelName());
+#endif
+ return; // skip the inconsistent event
+ }
+ break;
+ }
+ }
+
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
}
@@ -1999,16 +2090,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
- // Apply target flags.
- 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,
+ status = connection->inputPublisher.publishKeyEvent(
+ keyEntry->deviceId, keyEntry->source,
+ dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
+ keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
@@ -2024,24 +2110,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
case EventEntry::TYPE_MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
- // Apply target flags.
- int32_t action = motionEntry->action;
- int32_t flags = motionEntry->flags;
- 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;
- } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
- action = AMOTION_EVENT_ACTION_CANCEL;
- } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
- action = AMOTION_EVENT_ACTION_DOWN;
- }
- if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
- flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
- }
-
// If headMotionSample is non-NULL, then it points to the first new sample that we
// were unable to dispatch during the previous cycle so we resume dispatching from
// that point in the list of motion samples.
@@ -2082,13 +2150,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
}
}
- // 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, motionEntry->buttonState,
+ status = connection->inputPublisher.publishMotionEvent(
+ motionEntry->deviceId, motionEntry->source,
+ dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
+ motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
xOffset, yOffset,
motionEntry->xPrecision, motionEntry->yPrecision,
motionEntry->downTime, firstMotionSample->eventTime,
@@ -2102,8 +2168,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
return;
}
- if (action == AMOTION_EVENT_ACTION_MOVE
- || action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_MOVE
+ || dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
// Append additional motion samples.
MotionSample* nextMotionSample = firstMotionSample->next;
for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
@@ -2355,23 +2421,22 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
break;
}
- int32_t xOffset, yOffset;
- float scaleFactor;
+ InputTarget target;
const InputWindow* window = getWindowLocked(connection->inputChannel);
if (window) {
- xOffset = -window->frameLeft;
- yOffset = -window->frameTop;
- scaleFactor = window->scaleFactor;
+ target.xOffset = -window->frameLeft;
+ target.yOffset = -window->frameTop;
+ target.scaleFactor = window->scaleFactor;
} else {
- xOffset = 0;
- yOffset = 0;
- scaleFactor = 1.0f;
+ target.xOffset = 0;
+ target.yOffset = 0;
+ target.scaleFactor = 1.0f;
}
+ target.inputChannel = connection->inputChannel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- DispatchEntry* cancelationDispatchEntry =
- mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref
- 0, xOffset, yOffset, scaleFactor);
- connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
+ enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+ &target, false, InputTarget::FLAG_DISPATCH_AS_IS);
mAllocator.releaseEventEntry(cancelationEventEntry);
}
@@ -3327,6 +3392,7 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
resetTargetsLocked();
mTouchState.reset();
+ mLastHoverWindow = NULL;
}
void InputDispatcher::logDispatchStateLocked() {
@@ -4125,113 +4191,188 @@ bool InputDispatcher::InputState::isNeutral() const {
return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
}
-void InputDispatcher::InputState::trackEvent(const EventEntry* entry, int32_t action) {
- switch (entry->type) {
- case EventEntry::TYPE_KEY:
- trackKey(static_cast<const KeyEntry*>(entry), action);
- break;
-
- case EventEntry::TYPE_MOTION:
- trackMotion(static_cast<const MotionEntry*>(entry), action);
- break;
+bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source) const {
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ const MotionMemento& memento = mMotionMementos.itemAt(i);
+ if (memento.deviceId == deviceId
+ && memento.source == source
+ && memento.hovering) {
+ return true;
+ }
}
+ return false;
}
-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;
+bool InputDispatcher::InputState::trackKey(const KeyEntry* entry,
+ int32_t action, int32_t flags) {
+ switch (action) {
+ case AKEY_EVENT_ACTION_UP: {
+ if (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;
+ }
}
}
+ ssize_t index = findKeyMemento(entry);
+ if (index >= 0) {
+ mKeyMementos.removeAt(index);
+ return true;
+ }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
+ "keyCode=%d, scanCode=%d",
+ entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
+#endif
+ return false;
}
- for (size_t i = 0; i < mKeyMementos.size(); i++) {
- KeyMemento& memento = mKeyMementos.editItemAt(i);
- if (memento.deviceId == entry->deviceId
- && memento.source == entry->source
- && memento.keyCode == entry->keyCode
- && memento.scanCode == entry->scanCode) {
- switch (action) {
- case AKEY_EVENT_ACTION_UP:
- mKeyMementos.removeAt(i);
- return;
-
- case AKEY_EVENT_ACTION_DOWN:
- mKeyMementos.removeAt(i);
- goto Found;
-
- default:
- return;
- }
+ case AKEY_EVENT_ACTION_DOWN: {
+ ssize_t index = findKeyMemento(entry);
+ if (index >= 0) {
+ mKeyMementos.removeAt(index);
}
+ addKeyMemento(entry, flags);
+ return true;
}
-Found:
- if (action == AKEY_EVENT_ACTION_DOWN) {
- mKeyMementos.push();
- KeyMemento& memento = mKeyMementos.editTop();
- memento.deviceId = entry->deviceId;
- memento.source = entry->source;
- memento.keyCode = entry->keyCode;
- memento.scanCode = entry->scanCode;
- memento.flags = entry->flags;
- memento.downTime = entry->downTime;
+ default:
+ return true;
}
}
-void InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action) {
+bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry,
+ int32_t action, int32_t flags) {
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 (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;
+ switch (actionMasked) {
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL: {
+ ssize_t index = findMotionMemento(entry, false /*hovering*/);
+ if (index >= 0) {
+ mMotionMementos.removeAt(index);
+ return true;
+ }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
+ "actionMasked=%d",
+ entry->deviceId, entry->source, actionMasked);
+#endif
+ return false;
+ }
- case AMOTION_EVENT_ACTION_DOWN:
- mMotionMementos.removeAt(i);
- goto Found;
+ case AMOTION_EVENT_ACTION_DOWN: {
+ ssize_t index = findMotionMemento(entry, false /*hovering*/);
+ if (index >= 0) {
+ mMotionMementos.removeAt(index);
+ }
+ addMotionMemento(entry, flags, false /*hovering*/);
+ return true;
+ }
- case AMOTION_EVENT_ACTION_POINTER_UP:
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- case AMOTION_EVENT_ACTION_MOVE:
- memento.setPointers(entry);
- return;
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_MOVE: {
+ ssize_t index = findMotionMemento(entry, false /*hovering*/);
+ if (index >= 0) {
+ MotionMemento& memento = mMotionMementos.editItemAt(index);
+ memento.setPointers(entry);
+ return true;
+ }
+ if (actionMasked == AMOTION_EVENT_ACTION_MOVE
+ && (entry->source & (AINPUT_SOURCE_CLASS_JOYSTICK
+ | AINPUT_SOURCE_CLASS_NAVIGATION))) {
+ // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
+ return true;
+ }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("Dropping inconsistent motion pointer up/down or move event: "
+ "deviceId=%d, source=%08x, actionMasked=%d",
+ entry->deviceId, entry->source, actionMasked);
+#endif
+ return false;
+ }
- default:
- return;
- }
+ case AMOTION_EVENT_ACTION_HOVER_EXIT: {
+ ssize_t index = findMotionMemento(entry, true /*hovering*/);
+ if (index >= 0) {
+ mMotionMementos.removeAt(index);
+ return true;
}
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x",
+ entry->deviceId, entry->source);
+#endif
+ return false;
}
-Found:
- 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;
- memento.source = entry->source;
- memento.xPrecision = entry->xPrecision;
- memento.yPrecision = entry->yPrecision;
- memento.downTime = entry->downTime;
- memento.setPointers(entry);
- memento.hovering = actionMasked != AMOTION_EVENT_ACTION_DOWN;
+ case AMOTION_EVENT_ACTION_HOVER_MOVE: {
+ ssize_t index = findMotionMemento(entry, true /*hovering*/);
+ if (index >= 0) {
+ mMotionMementos.removeAt(index);
+ }
+ addMotionMemento(entry, flags, true /*hovering*/);
+ return true;
+ }
+
+ default:
+ return true;
}
}
+ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const {
+ for (size_t i = 0; i < mKeyMementos.size(); i++) {
+ const KeyMemento& memento = mKeyMementos.itemAt(i);
+ if (memento.deviceId == entry->deviceId
+ && memento.source == entry->source
+ && memento.keyCode == entry->keyCode
+ && memento.scanCode == entry->scanCode) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry,
+ bool hovering) const {
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ const MotionMemento& memento = mMotionMementos.itemAt(i);
+ if (memento.deviceId == entry->deviceId
+ && memento.source == entry->source
+ && memento.hovering == hovering) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
+ mKeyMementos.push();
+ KeyMemento& memento = mKeyMementos.editTop();
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.keyCode = entry->keyCode;
+ memento.scanCode = entry->scanCode;
+ memento.flags = flags;
+ memento.downTime = entry->downTime;
+}
+
+void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry,
+ int32_t flags, bool hovering) {
+ mMotionMementos.push();
+ MotionMemento& memento = mMotionMementos.editTop();
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.flags = flags;
+ memento.xPrecision = entry->xPrecision;
+ memento.yPrecision = entry->yPrecision;
+ memento.downTime = entry->downTime;
+ memento.setPointers(entry);
+ memento.hovering = hovering;
+}
+
void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
pointerCount = entry->pointerCount;
for (uint32_t i = 0; i < entry->pointerCount; i++) {
@@ -4243,20 +4384,17 @@ void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry*
void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
Allocator* allocator, Vector<EventEntry*>& outEvents,
const CancelationOptions& options) {
- for (size_t i = 0; i < mKeyMementos.size(); ) {
+ for (size_t i = 0; i < mKeyMementos.size(); i++) {
const KeyMemento& memento = mKeyMementos.itemAt(i);
if (shouldCancelKey(memento, options)) {
outEvents.push(allocator->obtainKeyEntry(currentTime,
memento.deviceId, memento.source, 0,
AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
- mKeyMementos.removeAt(i);
- } else {
- i += 1;
}
}
- for (size_t i = 0; i < mMotionMementos.size(); ) {
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
const MotionMemento& memento = mMotionMementos.itemAt(i);
if (shouldCancelMotion(memento, options)) {
outEvents.push(allocator->obtainMotionEntry(currentTime,
@@ -4264,12 +4402,9 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim
memento.hovering
? AMOTION_EVENT_ACTION_HOVER_EXIT
: AMOTION_EVENT_ACTION_CANCEL,
- 0, 0, 0, 0,
+ memento.flags, 0, 0, 0,
memento.xPrecision, memento.yPrecision, memento.downTime,
memento.pointerCount, memento.pointerProperties, memento.pointerCoords));
- mMotionMementos.removeAt(i);
- } else {
- i += 1;
}
}
}
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 676d162..bdd1922 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -522,6 +522,10 @@ private:
// True if dispatch has started.
bool inProgress;
+ // Set to the resolved action and flags when the event is enqueued.
+ int32_t resolvedAction;
+ int32_t resolvedFlags;
+
// For motion events:
// Pointer to the first motion sample to dispatch in this cycle.
// Usually NULL to indicate that the list of motion samples begins at
@@ -709,14 +713,19 @@ private:
// Returns true if there is no state to be canceled.
bool isNeutral() const;
- // Records tracking information for an event that has just been published.
- void trackEvent(const EventEntry* entry, int32_t action);
+ // Returns true if the specified source is known to have received a hover enter
+ // motion event.
+ bool isHovering(int32_t deviceId, uint32_t source) const;
// Records tracking information for a key event that has just been published.
- void trackKey(const KeyEntry* entry, int32_t action);
+ // Returns true if the event should be delivered, false if it is inconsistent
+ // and should be skipped.
+ bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags);
// Records tracking information for a motion event that has just been published.
- void trackMotion(const MotionEntry* entry, int32_t action);
+ // Returns true if the event should be delivered, false if it is inconsistent
+ // and should be skipped.
+ bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags);
// Synthesizes cancelation events for the current state and resets the tracked state.
void synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator,
@@ -756,6 +765,7 @@ private:
struct MotionMemento {
int32_t deviceId;
uint32_t source;
+ int32_t flags;
float xPrecision;
float yPrecision;
nsecs_t downTime;
@@ -771,6 +781,12 @@ private:
Vector<MotionMemento> mMotionMementos;
KeyedVector<int32_t, int32_t> mFallbackKeys;
+ ssize_t findKeyMemento(const KeyEntry* entry) const;
+ ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
+
+ void addKeyMemento(const KeyEntry* entry, int32_t flags);
+ void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
+
static bool shouldCancelKey(const KeyMemento& memento,
const CancelationOptions& options);
static bool shouldCancelMotion(const MotionMemento& memento,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 3e4c666..49cb864 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -53,6 +53,7 @@
#define INDENT2 " "
#define INDENT3 " "
#define INDENT4 " "
+#define INDENT5 " "
namespace android {
@@ -154,14 +155,15 @@ static uint32_t getButtonStateForScanCode(int32_t scanCode) {
case BTN_LEFT:
return AMOTION_EVENT_BUTTON_PRIMARY;
case BTN_RIGHT:
+ case BTN_STYLUS:
return AMOTION_EVENT_BUTTON_SECONDARY;
case BTN_MIDDLE:
+ case BTN_STYLUS2:
return AMOTION_EVENT_BUTTON_TERTIARY;
case BTN_SIDE:
return AMOTION_EVENT_BUTTON_BACK;
- case BTN_EXTRA:
- return AMOTION_EVENT_BUTTON_FORWARD;
case BTN_FORWARD:
+ case BTN_EXTRA:
return AMOTION_EVENT_BUTTON_FORWARD;
case BTN_BACK:
return AMOTION_EVENT_BUTTON_BACK;
@@ -176,8 +178,7 @@ static uint32_t getButtonStateForScanCode(int32_t scanCode) {
static bool isPointerDown(int32_t buttonState) {
return buttonState &
(AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY
- | AMOTION_EVENT_BUTTON_TERTIARY
- | AMOTION_EVENT_BUTTON_ERASER);
+ | AMOTION_EVENT_BUTTON_TERTIARY);
}
static int32_t calculateEdgeFlagsUsingPointerBounds(
@@ -767,10 +768,6 @@ void InputReader::dump(String8& dump) {
dump.append(mConfig.excludedDeviceNames.itemAt(i).string());
}
dump.append("]\n");
- dump.appendFormat(INDENT2 "FilterTouchEvents: %s\n",
- toString(mConfig.filterTouchEvents));
- dump.appendFormat(INDENT2 "FilterJumpyTouchEvents: %s\n",
- toString(mConfig.filterJumpyTouchEvents));
dump.appendFormat(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
mConfig.virtualKeyQuietTime * 0.000001f);
@@ -921,7 +918,7 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) {
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",
+ "keycode=0x%04x value=0x%08x flags=0x%08x",
rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode,
rawEvent->value, rawEvent->flags);
#endif
@@ -1921,8 +1918,17 @@ void TouchInputMapper::dump(String8& dump) {
dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mLocked.distanceScale);
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);
+ dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount);
+ for (uint32_t i = 0; i < mLastTouch.pointerCount; i++) {
+ const PointerData& pointer = mLastTouch.pointers[i];
+ dump.appendFormat(INDENT5 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
+ "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
+ "orientation=%d, distance=%d, isStylus=%s\n", i,
+ pointer.id, pointer.x, pointer.y, pointer.pressure,
+ pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, pointer.toolMinor,
+ pointer.orientation, pointer.distance, toString(pointer.isStylus));
+ }
if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n");
@@ -1945,13 +1951,6 @@ void TouchInputMapper::initializeLocked() {
mLastTouch.clear();
mDownTime = 0;
- for (uint32_t i = 0; i < MAX_POINTERS; i++) {
- mAveragingTouchFilter.historyStart[i] = 0;
- mAveragingTouchFilter.historyEnd[i] = 0;
- }
-
- mJumpyTouchFilter.jumpyPointsDropped = 0;
-
mLocked.currentVirtualKey.down = false;
mLocked.orientedRanges.havePressure = false;
@@ -2018,10 +2017,6 @@ void TouchInputMapper::configure(const InputReaderConfiguration* config, uint32_
}
void TouchInputMapper::configureParameters() {
- mParameters.useBadTouchFilter = mConfig.filterTouchEvents;
- mParameters.useAveragingTouchFilter = mConfig.filterTouchEvents;
- mParameters.useJumpyTouchFilter = mConfig.filterJumpyTouchEvents;
-
// Use the pointer presentation mode for devices that do not support distinct
// multitouch. The spot-based presentation relies on being able to accurately
// locate two or more fingers on the touch pad.
@@ -2112,13 +2107,6 @@ void TouchInputMapper::dumpParameters(String8& dump) {
mParameters.associatedDisplayId);
dump.appendFormat(INDENT4 "OrientationAware: %s\n",
toString(mParameters.orientationAware));
-
- dump.appendFormat(INDENT4 "UseBadTouchFilter: %s\n",
- toString(mParameters.useBadTouchFilter));
- dump.appendFormat(INDENT4 "UseAveragingTouchFilter: %s\n",
- toString(mParameters.useAveragingTouchFilter));
- dump.appendFormat(INDENT4 "UseJumpyTouchFilter: %s\n",
- toString(mParameters.useJumpyTouchFilter));
}
void TouchInputMapper::configureRawAxes() {
@@ -2975,33 +2963,10 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
#endif
// Preprocess pointer data.
- if (mParameters.useBadTouchFilter) {
- if (applyBadTouchFilter()) {
- havePointerIds = false;
- }
- }
-
- if (mParameters.useJumpyTouchFilter) {
- if (applyJumpyTouchFilter()) {
- havePointerIds = false;
- }
- }
-
if (!havePointerIds) {
calculatePointerIds();
}
- TouchData temp;
- TouchData* savedTouch;
- if (mParameters.useAveragingTouchFilter) {
- temp.copyFrom(mCurrentTouch);
- savedTouch = & temp;
-
- applyAveragingTouchFilter();
- } else {
- savedTouch = & mCurrentTouch;
- }
-
uint32_t policyFlags = 0;
if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
@@ -3048,9 +3013,9 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
// Keep the button state so we can track edge-triggered button state changes.
if (touchResult == DROP_STROKE) {
mLastTouch.clear();
- mLastTouch.buttonState = savedTouch->buttonState;
+ mLastTouch.buttonState = mCurrentTouch.buttonState;
} else {
- mLastTouch.copyFrom(*savedTouch);
+ mLastTouch.copyFrom(mCurrentTouch);
}
}
@@ -3704,6 +3669,29 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag
mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
mPointerGesture.currentGestureIdBits, -1,
0, 0, mPointerGesture.downTime);
+ } else if (dispatchedGestureIdBits.isEmpty()
+ && !mPointerGesture.lastGestureIdBits.isEmpty()) {
+ // Synthesize a hover move event after all pointers go up to indicate that
+ // the pointer is hovering again even if the user is not currently touching
+ // the touch pad. This ensures that a view will receive a fresh hover enter
+ // event after a tap.
+ float x, y;
+ mPointerController->getPosition(&x, &y);
+
+ PointerProperties pointerProperties;
+ pointerProperties.clear();
+ pointerProperties.id = 0;
+ pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER;
+
+ PointerCoords pointerCoords;
+ pointerCoords.clear();
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+
+ getDispatcher()->notifyMotion(when, getDeviceId(), mPointerSource, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1, &pointerProperties, &pointerCoords, 0, 0, mPointerGesture.downTime);
}
// Update state.
@@ -4793,359 +4781,6 @@ void TouchInputMapper::calculatePointerIds() {
}
}
-/* Special hack for devices that have bad screen data: if one of the
- * points has moved more than a screen height from the last position,
- * then drop it. */
-bool TouchInputMapper::applyBadTouchFilter() {
- uint32_t pointerCount = mCurrentTouch.pointerCount;
-
- // Nothing to do if there are no points.
- if (pointerCount == 0) {
- return false;
- }
-
- // Don't do anything if a finger is going down or up. We run
- // here before assigning pointer IDs, so there isn't a good
- // way to do per-finger matching.
- if (pointerCount != mLastTouch.pointerCount) {
- return false;
- }
-
- // We consider a single movement across more than a 7/16 of
- // the long size of the screen to be bad. This was a magic value
- // determined by looking at the maximum distance it is feasible
- // to actually move in one sample.
- int32_t maxDeltaY = (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1) * 7 / 16;
-
- // XXX The original code in InputDevice.java included commented out
- // code for testing the X axis. Note that when we drop a point
- // we don't actually restore the old X either. Strange.
- // The old code also tries to track when bad points were previously
- // detected but it turns out that due to the placement of a "break"
- // at the end of the loop, we never set mDroppedBadPoint to true
- // so it is effectively dead code.
- // Need to figure out if the old code is busted or just overcomplicated
- // but working as intended.
-
- // Look through all new points and see if any are farther than
- // acceptable from all previous points.
- for (uint32_t i = pointerCount; i-- > 0; ) {
- int32_t y = mCurrentTouch.pointers[i].y;
- int32_t closestY = INT_MAX;
- int32_t closestDeltaY = 0;
-
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
-#endif
-
- for (uint32_t j = pointerCount; j-- > 0; ) {
- int32_t lastY = mLastTouch.pointers[j].y;
- int32_t deltaY = abs(y - lastY);
-
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
- j, lastY, deltaY);
-#endif
-
- if (deltaY < maxDeltaY) {
- goto SkipSufficientlyClosePoint;
- }
- if (deltaY < closestDeltaY) {
- closestDeltaY = deltaY;
- closestY = lastY;
- }
- }
-
- // Must not have found a close enough match.
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
- i, y, closestY, closestDeltaY, maxDeltaY);
-#endif
-
- mCurrentTouch.pointers[i].y = closestY;
- return true; // XXX original code only corrects one point
-
- SkipSufficientlyClosePoint: ;
- }
-
- // No change.
- return false;
-}
-
-/* Special hack for devices that have bad screen data: drop points where
- * the coordinate value for one axis has jumped to the other pointer's location.
- */
-bool TouchInputMapper::applyJumpyTouchFilter() {
- uint32_t pointerCount = mCurrentTouch.pointerCount;
- if (mLastTouch.pointerCount != pointerCount) {
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
- mLastTouch.pointerCount, pointerCount);
- for (uint32_t i = 0; i < pointerCount; i++) {
- LOGD(" Pointer %d (%d, %d)", i,
- mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
- }
-#endif
-
- if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- if (mLastTouch.pointerCount == 1 && pointerCount == 2) {
- // Just drop the first few events going from 1 to 2 pointers.
- // They're bad often enough that they're not worth considering.
- mCurrentTouch.pointerCount = 1;
- mJumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Pointer 2 dropped");
-#endif
- return true;
- } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) {
- // The event when we go from 2 -> 1 tends to be messed up too
- mCurrentTouch.pointerCount = 2;
- mCurrentTouch.pointers[0] = mLastTouch.pointers[0];
- mCurrentTouch.pointers[1] = mLastTouch.pointers[1];
- mJumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
- for (int32_t i = 0; i < 2; i++) {
- LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
- mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
- }
-#endif
- return true;
- }
- }
- // Reset jumpy points dropped on other transitions or if limit exceeded.
- mJumpyTouchFilter.jumpyPointsDropped = 0;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Transition - drop limit reset");
-#endif
- return false;
- }
-
- // We have the same number of pointers as last time.
- // A 'jumpy' point is one where the coordinate value for one axis
- // has jumped to the other pointer's location. No need to do anything
- // else if we only have one pointer.
- if (pointerCount < 2) {
- return false;
- }
-
- if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
- int jumpyEpsilon = (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1) / JUMPY_EPSILON_DIVISOR;
-
- // We only replace the single worst jumpy point as characterized by pointer distance
- // in a single axis.
- int32_t badPointerIndex = -1;
- int32_t badPointerReplacementIndex = -1;
- int32_t badPointerDistance = INT_MIN; // distance to be corrected
-
- for (uint32_t i = pointerCount; i-- > 0; ) {
- int32_t x = mCurrentTouch.pointers[i].x;
- int32_t y = mCurrentTouch.pointers[i].y;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
-#endif
-
- // Check if a touch point is too close to another's coordinates
- bool dropX = false, dropY = false;
- for (uint32_t j = 0; j < pointerCount; j++) {
- if (i == j) {
- continue;
- }
-
- if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) {
- dropX = true;
- break;
- }
-
- if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) {
- dropY = true;
- break;
- }
- }
- if (! dropX && ! dropY) {
- continue; // not jumpy
- }
-
- // Find a replacement candidate by comparing with older points on the
- // complementary (non-jumpy) axis.
- int32_t distance = INT_MIN; // distance to be corrected
- int32_t replacementIndex = -1;
-
- if (dropX) {
- // X looks too close. Find an older replacement point with a close Y.
- int32_t smallestDeltaY = INT_MAX;
- for (uint32_t j = 0; j < pointerCount; j++) {
- int32_t deltaY = abs(y - mLastTouch.pointers[j].y);
- if (deltaY < smallestDeltaY) {
- smallestDeltaY = deltaY;
- replacementIndex = j;
- }
- }
- distance = abs(x - mLastTouch.pointers[replacementIndex].x);
- } else {
- // Y looks too close. Find an older replacement point with a close X.
- int32_t smallestDeltaX = INT_MAX;
- for (uint32_t j = 0; j < pointerCount; j++) {
- int32_t deltaX = abs(x - mLastTouch.pointers[j].x);
- if (deltaX < smallestDeltaX) {
- smallestDeltaX = deltaX;
- replacementIndex = j;
- }
- }
- distance = abs(y - mLastTouch.pointers[replacementIndex].y);
- }
-
- // If replacing this pointer would correct a worse error than the previous ones
- // considered, then use this replacement instead.
- if (distance > badPointerDistance) {
- badPointerIndex = i;
- badPointerReplacementIndex = replacementIndex;
- badPointerDistance = distance;
- }
- }
-
- // Correct the jumpy pointer if one was found.
- if (badPointerIndex >= 0) {
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
- badPointerIndex,
- mLastTouch.pointers[badPointerReplacementIndex].x,
- mLastTouch.pointers[badPointerReplacementIndex].y);
-#endif
-
- mCurrentTouch.pointers[badPointerIndex].x =
- mLastTouch.pointers[badPointerReplacementIndex].x;
- mCurrentTouch.pointers[badPointerIndex].y =
- mLastTouch.pointers[badPointerReplacementIndex].y;
- mJumpyTouchFilter.jumpyPointsDropped += 1;
- return true;
- }
- }
-
- mJumpyTouchFilter.jumpyPointsDropped = 0;
- return false;
-}
-
-/* Special hack for devices that have bad screen data: aggregate and
- * compute averages of the coordinate data, to reduce the amount of
- * jitter seen by applications. */
-void TouchInputMapper::applyAveragingTouchFilter() {
- for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) {
- uint32_t id = mCurrentTouch.pointers[currentIndex].id;
- int32_t x = mCurrentTouch.pointers[currentIndex].x;
- int32_t y = mCurrentTouch.pointers[currentIndex].y;
- int32_t pressure;
- switch (mCalibration.pressureSource) {
- case Calibration::PRESSURE_SOURCE_PRESSURE:
- pressure = mCurrentTouch.pointers[currentIndex].pressure;
- break;
- case Calibration::PRESSURE_SOURCE_TOUCH:
- pressure = mCurrentTouch.pointers[currentIndex].touchMajor;
- break;
- default:
- pressure = 1;
- break;
- }
-
- if (mLastTouch.idBits.hasBit(id)) {
- // Pointer was down before and is still down now.
- // Compute average over history trace.
- uint32_t start = mAveragingTouchFilter.historyStart[id];
- uint32_t end = mAveragingTouchFilter.historyEnd[id];
-
- int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x;
- int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y;
- uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
- id, distance);
-#endif
-
- if (distance < AVERAGING_DISTANCE_LIMIT) {
- // Increment end index in preparation for recording new historical data.
- end += 1;
- if (end > AVERAGING_HISTORY_SIZE) {
- end = 0;
- }
-
- // If the end index has looped back to the start index then we have filled
- // the historical trace up to the desired size so we drop the historical
- // data at the start of the trace.
- if (end == start) {
- start += 1;
- if (start > AVERAGING_HISTORY_SIZE) {
- start = 0;
- }
- }
-
- // Add the raw data to the historical trace.
- mAveragingTouchFilter.historyStart[id] = start;
- mAveragingTouchFilter.historyEnd[id] = end;
- mAveragingTouchFilter.historyData[end].pointers[id].x = x;
- mAveragingTouchFilter.historyData[end].pointers[id].y = y;
- mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure;
-
- // Average over all historical positions in the trace by total pressure.
- int32_t averagedX = 0;
- int32_t averagedY = 0;
- int32_t totalPressure = 0;
- for (;;) {
- int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x;
- int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y;
- int32_t historicalPressure = mAveragingTouchFilter.historyData[start]
- .pointers[id].pressure;
-
- averagedX += historicalX * historicalPressure;
- averagedY += historicalY * historicalPressure;
- totalPressure += historicalPressure;
-
- if (start == end) {
- break;
- }
-
- start += 1;
- if (start > AVERAGING_HISTORY_SIZE) {
- start = 0;
- }
- }
-
- if (totalPressure != 0) {
- averagedX /= totalPressure;
- averagedY /= totalPressure;
-
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - "
- "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
- averagedX, averagedY);
-#endif
-
- mCurrentTouch.pointers[currentIndex].x = averagedX;
- mCurrentTouch.pointers[currentIndex].y = averagedY;
- }
- } else {
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
-#endif
- }
- } else {
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
-#endif
- }
-
- // Reset pointer history.
- mAveragingTouchFilter.historyStart[id] = 0;
- mAveragingTouchFilter.historyEnd[id] = 0;
- mAveragingTouchFilter.historyData[0].pointers[id].x = x;
- mAveragingTouchFilter.historyData[0].pointers[id].y = y;
- mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure;
- }
-}
-
int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
{ // acquire lock
AutoMutex _l(mLock);
@@ -5361,7 +4996,6 @@ void SingleTouchInputMapper::configureRawAxes() {
MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) :
TouchInputMapper(device), mSlotCount(0), mUsingSlotsProtocol(false) {
- clearState();
}
MultiTouchInputMapper::~MultiTouchInputMapper() {
@@ -5371,6 +5005,25 @@ void MultiTouchInputMapper::clearState() {
mAccumulator.clearSlots(mSlotCount);
mAccumulator.clearButtons();
mButtonState = 0;
+ mPointerIdBits.clear();
+
+ if (mUsingSlotsProtocol) {
+ // Query the driver for the current slot index and use it as the initial slot
+ // before we start reading events from the device. It is possible that the
+ // current slot index will not be the same as it was when the first event was
+ // written into the evdev buffer, which means the input mapper could start
+ // out of sync with the initial state of the events in the evdev buffer.
+ // In the extremely unlikely case that this happens, the data from
+ // two slots will be confused until the next ABS_MT_SLOT event is received.
+ // This can cause the touch point to "jump", but at least there will be
+ // no stuck touches.
+ status_t status = getEventHub()->getAbsoluteAxisValue(getDeviceId(), ABS_MT_SLOT,
+ &mAccumulator.currentSlot);
+ if (status) {
+ LOGW("Could not retrieve current multitouch slot index. status=%d", status);
+ mAccumulator.currentSlot = -1;
+ }
+ }
}
void MultiTouchInputMapper::reset() {
@@ -5577,28 +5230,32 @@ void MultiTouchInputMapper::sync(nsecs_t when) {
// Assign pointer id using tracking id if available.
if (havePointerIds) {
- int32_t id;
- if (mUsingSlotsProtocol) {
- id = inIndex;
- } else if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
- id = inSlot.absMTTrackingId;
- } else {
- id = -1;
- }
+ int32_t id = -1;
+ if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+ int32_t trackingId = inSlot.absMTTrackingId;
+
+ for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
+ uint32_t n = idBits.firstMarkedBit();
+ idBits.clearBit(n);
- if (id >= 0 && id <= MAX_POINTER_ID) {
+ if (mPointerTrackingIdMap[n] == trackingId) {
+ id = n;
+ }
+ }
+
+ if (id < 0 && !mPointerIdBits.isFull()) {
+ id = mPointerIdBits.firstUnmarkedBit();
+ mPointerIdBits.markBit(id);
+ mPointerTrackingIdMap[id] = trackingId;
+ }
+ }
+ if (id < 0) {
+ havePointerIds = false;
+ mCurrentTouch.idBits.clear();
+ } else {
outPointer.id = id;
mCurrentTouch.idToIndex[id] = outCount;
mCurrentTouch.idBits.markBit(id);
- } else {
- if (id >= 0) {
-#if DEBUG_POINTERS
- LOGD("Pointers: Ignoring driver provided slot index or tracking id %d because "
- "it is larger than the maximum supported pointer id %d",
- id, MAX_POINTER_ID);
-#endif
- }
- havePointerIds = false;
}
}
@@ -5610,6 +5267,8 @@ void MultiTouchInputMapper::sync(nsecs_t when) {
mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp;
mCurrentTouch.buttonState = mButtonState;
+ mPointerIdBits = mCurrentTouch.idBits;
+
syncTouch(when, havePointerIds);
if (!mUsingSlotsProtocol) {
@@ -5649,6 +5308,8 @@ void MultiTouchInputMapper::configureRawAxes() {
}
mAccumulator.allocateSlots(mSlotCount);
+
+ clearState();
}
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 288ff4e..69fa6b4 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -57,14 +57,6 @@ struct InputReaderConfiguration {
CHANGE_MUST_REOPEN = 1 << 31,
};
- // Determines whether to turn on some hacks we have to improve the touch interaction with a
- // certain device whose screen currently is not all that good.
- bool filterTouchEvents;
-
- // Determines whether to turn on some hacks to improve touch interaction with another device
- // where touch coordinate data can get corrupted.
- bool filterJumpyTouchEvents;
-
// Gets the amount of time to disable virtual keys after the screen is touched
// in order to filter out accidental virtual key presses due to swiping gestures
// or taps near the edge of the display. May be 0 to disable the feature.
@@ -146,8 +138,6 @@ struct InputReaderConfiguration {
float pointerGestureZoomSpeedRatio;
InputReaderConfiguration() :
- filterTouchEvents(false),
- filterJumpyTouchEvents(false),
virtualKeyQuietTime(0),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
@@ -812,10 +802,6 @@ protected:
int32_t associatedDisplayId;
bool orientationAware;
- bool useBadTouchFilter;
- bool useJumpyTouchFilter;
- bool useAveragingTouchFilter;
-
enum GestureMode {
GESTURE_MODE_POINTER,
GESTURE_MODE_SPOTS,
@@ -1042,38 +1028,6 @@ protected:
void syncTouch(nsecs_t when, bool havePointerIds);
private:
- /* Maximum number of historical samples to average. */
- static const uint32_t AVERAGING_HISTORY_SIZE = 5;
-
- /* Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value. */
- static const uint32_t JUMPY_EPSILON_DIVISOR = 212;
-
- /* Number of jumpy points to drop for touchscreens that need it. */
- static const uint32_t JUMPY_TRANSITION_DROPS = 3;
- static const uint32_t JUMPY_DROP_LIMIT = 3;
-
- /* Maximum squared distance for averaging.
- * If moving farther than this, turn of averaging to avoid lag in response. */
- static const uint64_t AVERAGING_DISTANCE_LIMIT = 75 * 75;
-
- struct AveragingTouchFilterState {
- // Individual history tracks are stored by pointer id
- uint32_t historyStart[MAX_POINTERS];
- uint32_t historyEnd[MAX_POINTERS];
- struct {
- struct {
- int32_t x;
- int32_t y;
- int32_t pressure;
- } pointers[MAX_POINTERS];
- } historyData[AVERAGING_HISTORY_SIZE];
- } mAveragingTouchFilter;
-
- struct JumpyTouchFilterState {
- uint32_t jumpyPointsDropped;
- } mJumpyTouchFilter;
-
struct PointerDistanceHeapElement {
uint32_t currentPointerIndex : 8;
uint32_t lastPointerIndex : 8;
@@ -1251,9 +1205,6 @@ private:
bool isPointInsideSurfaceLocked(int32_t x, int32_t y);
const VirtualKey* findVirtualKeyHitLocked(int32_t x, int32_t y);
- bool applyBadTouchFilter();
- bool applyJumpyTouchFilter();
- void applyAveragingTouchFilter();
void calculatePointerIds();
};
@@ -1401,6 +1352,10 @@ private:
int32_t mButtonState;
+ // Specifies the pointer id bits that are in use, and their associated tracking id.
+ BitSet32 mPointerIdBits;
+ int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
+
void clearState();
void sync(nsecs_t when);
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
index 08cc75e..0ae2ab8 100644
--- a/services/input/SpriteController.cpp
+++ b/services/input/SpriteController.cpp
@@ -252,11 +252,7 @@ void SpriteController::doUpdateSprites() {
| 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;
- }
+ SurfaceComposerClient::openGlobalTransaction();
haveTransaction = true;
}
@@ -322,10 +318,7 @@ void SpriteController::doUpdateSprites() {
}
if (haveTransaction) {
- status_t status = mSurfaceComposerClient->closeTransaction();
- if (status) {
- LOGE("Error %d closing transaction to update sprite surface.", status);
- }
+ SurfaceComposerClient::closeGlobalTransaction();
}
// If any surfaces were changed, write back the new surface properties to the sprites.
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index e349248..67067de 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -144,14 +144,6 @@ public:
mDisplayInfos.add(displayId, info);
}
- void setFilterTouchEvents(bool enabled) {
- mConfig.filterTouchEvents = enabled;
- }
-
- void setFilterJumpyTouchEvents(bool enabled) {
- mConfig.filterJumpyTouchEvents = enabled;
- }
-
virtual nsecs_t getVirtualKeyQuietTime() {
return 0;
}
@@ -429,6 +421,7 @@ class FakeEventHub : public EventHubInterface {
KeyedVector<int32_t, int32_t> keyCodeStates;
KeyedVector<int32_t, int32_t> scanCodeStates;
KeyedVector<int32_t, int32_t> switchStates;
+ KeyedVector<int32_t, int32_t> absoluteAxisValue;
KeyedVector<int32_t, KeyInfo> keys;
KeyedVector<int32_t, bool> leds;
Vector<VirtualKeyDefinition> virtualKeys;
@@ -514,6 +507,11 @@ public:
device->switchStates.replaceValueFor(switchCode, state);
}
+ void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
+ Device* device = getDevice(deviceId);
+ device->absoluteAxisValue.replaceValueFor(axis, value);
+ }
+
void addKey(int32_t deviceId, int32_t scanCode, int32_t keyCode, uint32_t flags) {
Device* device = getDevice(deviceId);
KeyInfo info;
@@ -677,6 +675,20 @@ private:
return AKEY_STATE_UNKNOWN;
}
+ virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
+ if (index >= 0) {
+ *outValue = device->absoluteAxisValue.valueAt(index);
+ return OK;
+ }
+ }
+ *outValue = 0;
+ return -1;
+ }
+
virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) const {
bool result = false;
@@ -3516,7 +3528,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
@@ -3525,9 +3537,9 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(2, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
@@ -3547,9 +3559,9 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(2, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
@@ -3567,9 +3579,9 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(2, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
@@ -3579,7 +3591,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(2, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
@@ -3594,7 +3606,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(2, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
@@ -3610,17 +3622,17 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(2, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(3, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
// Second finger up.
x3 += 30; y3 -= 20;
@@ -3630,22 +3642,22 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(2, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(3, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(3, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
@@ -3657,7 +3669,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(3, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
@@ -3708,7 +3720,7 @@ TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
FakeInputDispatcher::NotifyMotionArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(id, args.pointerProperties[0].id);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation));
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 158c778..0b15221 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -1072,7 +1072,7 @@ class AppWidgetService extends IAppWidgetService.Stub
throw new IllegalArgumentException("packageName and uid don't match packageName="
+ packageName);
}
- if (callingUid != packageUid && Process.supportsProcesses()) {
+ if (callingUid != packageUid) {
throw new IllegalArgumentException("packageName and uid don't match packageName="
+ packageName);
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 3aa1239..786f2fa 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -23,6 +23,7 @@ import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IBackupAgent;
import android.app.PendingIntent;
+import android.app.backup.BackupAgent;
import android.app.backup.BackupDataOutput;
import android.app.backup.FullBackup;
import android.app.backup.RestoreSet;
@@ -64,6 +65,7 @@ import android.os.SystemClock;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.EventLog;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -222,6 +224,7 @@ class BackupManagerService extends IBackupManager.Stub {
public PackageInfo pkgInfo;
public int pmToken; // in post-install restore, the PM's token for this transaction
public boolean needFullBackup;
+ public String[] filterSet;
RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
@@ -231,6 +234,7 @@ class BackupManagerService extends IBackupManager.Stub {
pkgInfo = _pkg;
pmToken = _pmToken;
needFullBackup = _needFullBackup;
+ filterSet = null;
}
RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
@@ -241,6 +245,18 @@ class BackupManagerService extends IBackupManager.Stub {
pkgInfo = null;
pmToken = 0;
needFullBackup = _needFullBackup;
+ filterSet = null;
+ }
+
+ RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
+ String[] _filterSet, boolean _needFullBackup) {
+ transport = _transport;
+ observer = _obs;
+ token = _token;
+ pkgInfo = null;
+ pmToken = 0;
+ needFullBackup = _needFullBackup;
+ filterSet = _filterSet;
}
}
@@ -402,7 +418,7 @@ class BackupManagerService extends IBackupManager.Stub {
Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
(new PerformRestoreTask(params.transport, params.observer,
params.token, params.pkgInfo, params.pmToken,
- params.needFullBackup)).run();
+ params.needFullBackup, params.filterSet)).run();
break;
}
@@ -1587,8 +1603,7 @@ class BackupManagerService extends IBackupManager.Stub {
// Initiate the target's backup pass
prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
- agent.doBackup(savedState, backupData, newState, false,
- token, mBackupManagerBinder);
+ agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
boolean success = waitUntilOperationComplete(token);
if (!success) {
@@ -1764,30 +1779,31 @@ class BackupManagerService extends IBackupManager.Stub {
if (agent != null) {
try {
ApplicationInfo app = pkg.applicationInfo;
- boolean sendApk = mIncludeApks
+ final 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());
+ 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, "Writing manifest for " + pkg.packageName);
+ writeAppManifest(pkg, mManifestFile, sendApk);
+ FullBackup.backupToTar(pkg.packageName, null, null,
+ mFilesDir.getAbsolutePath(),
+ mManifestFile.getAbsolutePath(),
+ output);
+
+ if (sendApk) {
+ writeApkToBackup(pkg, output);
}
- if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+ if (DEBUG) Slog.d(TAG, "Calling doFullBackup()");
final int token = generateToken();
prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
- agent.doBackup(null, mOutputFile, null, sendApk,
- token, mBackupManagerBinder);
+ agent.doFullBackup(mOutputFile, token, mBackupManagerBinder);
if (!waitUntilOperationComplete(token)) {
Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
} else {
@@ -1802,6 +1818,29 @@ class BackupManagerService extends IBackupManager.Stub {
tearDown(pkg);
}
+ private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
+ // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
+ final String appSourceDir = pkg.applicationInfo.sourceDir;
+ final String apkDir = new File(appSourceDir).getParent();
+ FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
+ apkDir, appSourceDir, output);
+
+ // Save associated .obb content if it exists and we did save the apk
+ // check for .obb and save those too
+ final File obbDir = Environment.getExternalStorageAppObbDirectory(pkg.packageName);
+ if (obbDir != null) {
+ if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
+ File[] obbFiles = obbDir.listFiles();
+ if (obbFiles != null) {
+ final String obbDirName = obbDir.getAbsolutePath();
+ for (File obb : obbFiles) {
+ FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
+ obbDirName, obb.getAbsolutePath(), output);
+ }
+ }
+ }
+ }
+
private void backupSharedStorage() throws RemoteException {
PackageInfo pkg = null;
try {
@@ -1813,7 +1852,7 @@ class BackupManagerService extends IBackupManager.Stub {
final int token = generateToken();
prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
- agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder);
+ agent.doFullBackup(mOutputFile, token, mBackupManagerBinder);
if (!waitUntilOperationComplete(token)) {
Slog.e(TAG, "Full backup failed on shared storage");
} else {
@@ -1933,7 +1972,7 @@ class BackupManagerService extends IBackupManager.Stub {
static class FileMetadata {
String packageName; // name of the owning app
String installerPackageName; // name of the market-type app that installed the owner
- int type; // e.g. FullBackup.TYPE_DIRECTORY
+ int type; // e.g. BackupAgent.TYPE_DIRECTORY
String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN
String path; // subpath within the semantic domain
long mode; // e.g. 0666 (actually int)
@@ -2182,15 +2221,15 @@ class BackupManagerService extends IBackupManager.Stub {
// If we haven't sent any data to this app yet, we probably
// need to clear it first. Check that.
if (!mClearedPackages.contains(pkg)) {
- // apps with their own full backup agents are
+ // apps with their own backup agents are
// responsible for coherently managing a full
// restore.
- if (mTargetApp.fullBackupAgentName == null) {
+ if (mTargetApp.backupAgentName == null) {
if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
clearApplicationDataSynchronous(pkg);
} else {
- if (DEBUG) Slog.d(TAG, "full backup agent ("
- + mTargetApp.fullBackupAgentName + ") => no clear");
+ if (DEBUG) Slog.d(TAG, "backup agent ("
+ + mTargetApp.backupAgentName + ") => no clear");
}
mClearedPackages.add(pkg);
} else {
@@ -2686,7 +2725,7 @@ class BackupManagerService extends IBackupManager.Stub {
StringBuilder b = new StringBuilder(128);
// mode string
- b.append((info.type == FullBackup.TYPE_DIRECTORY) ? 'd' : '-');
+ b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
b.append(((info.mode & 0400) != 0) ? 'r' : '-');
b.append(((info.mode & 0200) != 0) ? 'w' : '-');
b.append(((info.mode & 0100) != 0) ? 'x' : '-');
@@ -2746,9 +2785,9 @@ class BackupManagerService extends IBackupManager.Stub {
}
switch (typeChar) {
- case '0': info.type = FullBackup.TYPE_FILE; break;
+ case '0': info.type = BackupAgent.TYPE_FILE; break;
case '5': {
- info.type = FullBackup.TYPE_DIRECTORY;
+ info.type = BackupAgent.TYPE_DIRECTORY;
if (info.size != 0) {
Slog.w(TAG, "Directory entry with nonzero size in header");
info.size = 0;
@@ -2995,6 +3034,7 @@ class BackupManagerService extends IBackupManager.Stub {
private File mStateDir;
private int mPmToken;
private boolean mNeedFullBackup;
+ private HashSet<String> mFilterSet;
class RestoreRequest {
public PackageInfo app;
@@ -3008,7 +3048,7 @@ class BackupManagerService extends IBackupManager.Stub {
PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
long restoreSetToken, PackageInfo targetPackage, int pmToken,
- boolean needFullBackup) {
+ boolean needFullBackup, String[] filterSet) {
mTransport = transport;
mObserver = observer;
mToken = restoreSetToken;
@@ -3016,6 +3056,15 @@ class BackupManagerService extends IBackupManager.Stub {
mPmToken = pmToken;
mNeedFullBackup = needFullBackup;
+ if (filterSet != null) {
+ mFilterSet = new HashSet<String>();
+ for (String pkg : filterSet) {
+ mFilterSet.add(pkg);
+ }
+ } else {
+ mFilterSet = null;
+ }
+
try {
mStateDir = new File(mBaseStateDir, transport.transportDirName());
} catch (RemoteException e) {
@@ -3027,7 +3076,8 @@ class BackupManagerService extends IBackupManager.Stub {
long startRealtime = SystemClock.elapsedRealtime();
if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
+ " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
- + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken);
+ + " mTargetPackage=" + mTargetPackage + " mFilterSet=" + mFilterSet
+ + " mPmToken=" + mPmToken);
PackageManagerBackupAgent pmAgent = null;
int error = -1; // assume error
@@ -3046,6 +3096,22 @@ class BackupManagerService extends IBackupManager.Stub {
List<PackageInfo> agentPackages = allAgentPackages();
if (mTargetPackage == null) {
+ // if there's a filter set, strip out anything that isn't
+ // present before proceeding
+ if (mFilterSet != null) {
+ for (int i = agentPackages.size() - 1; i >= 0; i--) {
+ final PackageInfo pkg = agentPackages.get(i);
+ if (! mFilterSet.contains(pkg.packageName)) {
+ agentPackages.remove(i);
+ }
+ }
+ if (DEBUG) {
+ Slog.i(TAG, "Post-filter package set for restore:");
+ for (PackageInfo p : agentPackages) {
+ Slog.i(TAG, " " + p);
+ }
+ }
+ }
restorePackages.addAll(agentPackages);
} else {
// Just one package to attempt restore of
@@ -4241,6 +4307,67 @@ class BackupManagerService extends IBackupManager.Stub {
return -1;
}
+ public synchronized int restoreSome(long token, IRestoreObserver observer,
+ String[] packages) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "performRestore");
+
+ if (DEBUG) {
+ StringBuilder b = new StringBuilder(128);
+ b.append("restoreSome token=");
+ b.append(Long.toHexString(token));
+ b.append(" observer=");
+ b.append(observer.toString());
+ b.append(" packages=");
+ if (packages == null) {
+ b.append("null");
+ } else {
+ b.append('{');
+ boolean first = true;
+ for (String s : packages) {
+ if (!first) {
+ b.append(", ");
+ } else first = false;
+ b.append(s);
+ }
+ b.append('}');
+ }
+ Slog.d(TAG, b.toString());
+ }
+
+ if (mEnded) {
+ throw new IllegalStateException("Restore session already ended");
+ }
+
+ if (mRestoreTransport == null || mRestoreSets == null) {
+ Slog.e(TAG, "Ignoring restoreAll() with no restore set");
+ return -1;
+ }
+
+ if (mPackageName != null) {
+ Slog.e(TAG, "Ignoring restoreAll() on single-package session");
+ return -1;
+ }
+
+ synchronized (mQueueLock) {
+ for (int i = 0; i < mRestoreSets.length; i++) {
+ if (token == mRestoreSets[i].token) {
+ long oldId = Binder.clearCallingIdentity();
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(mRestoreTransport, observer, token,
+ packages, true);
+ mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
+ return 0;
+ }
+ }
+ }
+
+ Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
+ return -1;
+ }
+
public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index e6f443a..41450d2 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -44,7 +44,6 @@ import android.net.NetworkUtils;
import android.net.Proxy;
import android.net.ProxyProperties;
import android.net.RouteInfo;
-import android.net.vpn.VpnManager;
import android.net.wifi.WifiStateTracker;
import android.os.Binder;
import android.os.FileUtils;
@@ -65,6 +64,7 @@ import android.util.EventLog;
import android.util.Slog;
import android.util.SparseIntArray;
+import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering;
@@ -131,8 +131,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private List mNetRequestersPids[];
- private WifiWatchdogService mWifiWatchdogService;
-
// priority order of the nettrackers
// (excluding dynamically set mNetworkPreference)
// TODO - move mNetworkTypePreference into this
@@ -278,6 +276,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
RadioAttributes[] mRadioAttributes;
+ // the set of network types that can only be enabled by system/sig apps
+ List mProtectedNetworks;
+
public ConnectivityService(
Context context, INetworkManagementService netd, INetworkPolicyManager policyManager) {
if (DBG) log("ConnectivityService starting up");
@@ -381,6 +382,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
+ mProtectedNetworks = new ArrayList<Integer>();
+ int[] protectedNetworks = context.getResources().getIntArray(
+ com.android.internal.R.array.config_protectedNetworks);
+ for (int p : protectedNetworks) {
+ if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) {
+ mProtectedNetworks.add(p);
+ } else {
+ if (DBG) loge("Ignoring protectedNetwork " + p);
+ }
+ }
+
// high priority first
mPriorityList = new int[mNetworksDefined];
{
@@ -432,10 +444,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
wifiService.checkAndStartWifi();
mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
wst.startMonitoring(context, mHandler);
-
- //TODO: as part of WWS refactor, create only when needed
- mWifiWatchdogService = new WifiWatchdogService(context);
-
break;
case ConnectivityManager.TYPE_MOBILE:
mNetTrackers[netType] = new MobileDataStateTracker(netType,
@@ -488,11 +496,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mSettingsObserver.observe(mContext);
loadGlobalProxy();
-
- VpnManager.startVpnService(context);
}
-
/**
* Sets the preferred network.
* @param preference the new preference
@@ -802,6 +807,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
usedNetworkType = networkType;
}
}
+
+ if (mProtectedNetworks.contains(usedNetworkType)) {
+ enforceConnectivityInternalPermission();
+ }
+
NetworkStateTracker network = mNetTrackers[usedNetworkType];
if (network != null) {
Integer currentPid = new Integer(getCallingPid());
@@ -1012,6 +1022,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
enforceChangePermission();
+ if (mProtectedNetworks.contains(networkType)) {
+ enforceConnectivityInternalPermission();
+ }
+
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
return false;
}
@@ -1129,7 +1143,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
public void setDataDependency(int networkType, boolean met) {
- enforceChangePermission();
+ enforceConnectivityInternalPermission();
+
if (DBG) {
log("setDataDependency(" + networkType + ", " + met + ")");
}
@@ -1587,11 +1602,23 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (linkProperties != null) {
String iface = linkProperties.getInterfaceName();
if (TextUtils.isEmpty(iface) == false) {
- if (DBG) log("resetConnections(" + iface + ")");
- NetworkUtils.resetConnections(iface);
+ if (DBG) {
+ log("resetConnections(" + iface + ", NetworkUtils.RESET_ALL_ADDRESSES)");
+ }
+ NetworkUtils.resetConnections(iface, NetworkUtils.RESET_ALL_ADDRESSES);
}
}
}
+
+ // TODO: Temporary notifying upstread change to Tethering.
+ // @see bug/4455071
+ /** Notify TetheringService if interface name has been changed. */
+ if (TextUtils.equals(mNetTrackers[netType].getNetworkInfo().getReason(),
+ Phone.REASON_LINK_PROPERTIES_CHANGED)) {
+ if (isTetheringSupported()) {
+ mTethering.handleTetherIfaceChange();
+ }
+ }
}
private void addPrivateDnsRoutes(NetworkStateTracker nt) {
@@ -2017,7 +2044,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
break;
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
info = (NetworkInfo) msg.obj;
- handleConnectivityChange(info.getType(), true);
+ // TODO: Temporary allowing network configuration
+ // change not resetting sockets.
+ // @see bug/4455071
+ handleConnectivityChange(info.getType(), false);
break;
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
String causedBy = null;
@@ -2442,8 +2472,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
/**
* Protect a socket from VPN routing rules. This method is used by
- * VpnBuilder and not available in ConnectivityManager. Permission
- * checks are done in Vpn class.
+ * VpnBuilder and not available in ConnectivityManager. Permissions
+ * are checked in Vpn class.
* @hide
*/
@Override
@@ -2453,20 +2483,20 @@ public class ConnectivityService extends IConnectivityManager.Stub {
/**
* Prepare for a VPN application. This method is used by VpnDialogs
- * and not available in ConnectivityManager. Permission checks are
- * done in Vpn class.
+ * and not available in ConnectivityManager. Permissions are checked
+ * in Vpn class.
* @hide
*/
@Override
- public String prepareVpn(String packageName) {
- return mVpn.prepare(packageName);
+ public boolean prepareVpn(String oldPackage, String newPackage) {
+ return mVpn.prepare(oldPackage, newPackage);
}
/**
* Configure a TUN interface and return its file descriptor. Parameters
* are encoded and opaque to this class. This method is used by VpnBuilder
- * and not available in ConnectivityManager. Permission checks are done
- * in Vpn class.
+ * and not available in ConnectivityManager. Permissions are checked in
+ * Vpn class.
* @hide
*/
@Override
@@ -2474,6 +2504,28 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return mVpn.establish(config);
}
+ /**
+ * Start legacy VPN and return an intent to VpnDialogs. This method is
+ * used by VpnSettings and not available in ConnectivityManager.
+ * Permissions are checked in Vpn class.
+ * @hide
+ */
+ @Override
+ public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+ mVpn.startLegacyVpn(config, racoon, mtpd);
+ }
+
+ /**
+ * Return the information of the ongoing legacy VPN. This method is used
+ * by VpnSettings and not available in ConnectivityManager. Permissions
+ * are checked in Vpn class.
+ * @hide
+ */
+ @Override
+ public LegacyVpnInfo getLegacyVpnInfo() {
+ return mVpn.getLegacyVpnInfo();
+ }
+
private String getDefaultInterface() {
if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) {
NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
@@ -2502,7 +2554,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private VpnCallback() {
}
- public synchronized void override(String[] dnsServers) {
+ public synchronized void override(List<String> dnsServers, List<String> searchDomains) {
// TODO: override DNS servers and http proxy.
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 8037d7a..2d55433 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -137,6 +137,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
+ private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
final Context mContext;
final Resources mRes;
@@ -1057,25 +1058,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
final int N = imis.size();
- int count = 0;
+ if (N > 2) return true;
+ if (N < 1) return false;
+ int nonAuxCount = 0;
+ int auxCount = 0;
+ InputMethodSubtype nonAuxSubtype = null;
+ InputMethodSubtype auxSubtype = null;
for(int i = 0; i < N; ++i) {
final InputMethodInfo imi = imis.get(i);
final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeListLocked(
imi, true);
final int subtypeCount = subtypes.size();
if (subtypeCount == 0) {
- ++count;
+ ++nonAuxCount;
} else {
for (int j = 0; j < subtypeCount; ++j) {
- if (!subtypes.get(j).isAuxiliary()) {
- ++count;
+ final InputMethodSubtype subtype = subtypes.get(j);
+ if (!subtype.isAuxiliary()) {
+ ++nonAuxCount;
+ nonAuxSubtype = subtype;
+ } else {
+ ++auxCount;
+ auxSubtype = subtype;
}
}
}
- if (count > 1) return true;
}
+ if (nonAuxCount > 1 || auxCount > 1) {
+ return true;
+ } else if (nonAuxCount == 1 && auxCount == 1) {
+ if (nonAuxSubtype != null && auxSubtype != null
+ && nonAuxSubtype.getLocale().equals(auxSubtype.getLocale())
+ && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) {
+ return false;
+ }
+ return true;
+ }
+ return false;
}
- return false;
}
@Override
@@ -1603,8 +1623,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (lastImi == null) return null;
try {
final int lastSubtypeHash = Integer.valueOf(lastIme.second);
- return lastImi.getSubtypeAt(getSubtypeIdFromHashCode(
- lastImi, lastSubtypeHash));
+ final int lastSubtypeId = getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
+ if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
+ return null;
+ }
+ return lastImi.getSubtypeAt(lastSubtypeId);
} catch (NumberFormatException e) {
return null;
}
@@ -1621,7 +1644,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
if (imi == null) return false;
final int N = subtypes.length;
- mFileManager.addInputMethodSubtypes(mCurMethodId, subtypes);
+ mFileManager.addInputMethodSubtypes(imi, subtypes);
buildInputMethodListLocked(mMethodList, mMethodMap);
return true;
}
@@ -2003,25 +2026,26 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final CharSequence label = imi.loadLabel(pm);
if (showSubtypes && enabledSubtypeSet.size() > 0) {
final int subtypeCount = imi.getSubtypeCount();
+ if (DEBUG) {
+ Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
+ }
for (int j = 0; j < subtypeCount; ++j) {
- InputMethodSubtype subtype = imi.getSubtypeAt(j);
- if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode()))
- && !subtype.isAuxiliary()) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+ final String subtypeHashCode = String.valueOf(subtype.hashCode());
+ // We show all enabled IMEs and subtypes when an IME is shown.
+ if (enabledSubtypeSet.contains(subtypeHashCode)
+ && (mInputShown || !subtype.isAuxiliary())) {
final CharSequence title;
- int nameResId = subtype.getNameResId();
- String mode = subtype.getMode();
- if (nameResId != 0) {
- title = TextUtils.concat(subtype.getDisplayName(context,
- imi.getPackageName(), imi.getServiceInfo().applicationInfo),
- (TextUtils.isEmpty(label) ? "" : " (" + label + ")"));
- } else {
- CharSequence language = subtype.getLocale();
- // TODO: Use more friendly Title and UI
- title = label + "," + (mode == null ? "" : mode) + ","
- + (language == null ? "" : language);
- }
+ final String mode = subtype.getMode();
+ title = TextUtils.concat(subtype.getDisplayName(context,
+ imi.getPackageName(), imi.getServiceInfo().applicationInfo),
+ (TextUtils.isEmpty(label) ? "" : " (" + label + ")"));
imList.add(new Pair<CharSequence, Pair<InputMethodInfo, Integer>>(
title, new Pair<InputMethodInfo, Integer>(imi, j)));
+ // Removing this subtype from enabledSubtypeSet because we no longer
+ // need to add an entry of this subtype to imList to avoid duplicated
+ // entries.
+ enabledSubtypeSet.remove(subtypeHashCode);
}
}
} else {
@@ -2318,7 +2342,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
}
- ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
+ final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
applicableModeAndSubtypesMap.values());
if (!containsKeyboardSubtype) {
InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
@@ -2996,17 +3020,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
public void addInputMethodSubtypes(
- String imiId, InputMethodSubtype[] additionalSubtypes) {
+ InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) {
synchronized (mMethodMap) {
+ final HashSet<InputMethodSubtype> existingSubtypes =
+ new HashSet<InputMethodSubtype>();
+ for (int i = 0; i < imi.getSubtypeCount(); ++i) {
+ existingSubtypes.add(imi.getSubtypeAt(i));
+ }
+
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
final int N = additionalSubtypes.length;
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = additionalSubtypes[i];
- if (!subtypes.contains(subtype)) {
+ if (!subtypes.contains(subtype) && !existingSubtypes.contains(subtype)) {
subtypes.add(subtype);
}
}
- mSubtypesMap.put(imiId, subtypes);
+ mSubtypesMap.put(imi.getId(), subtypes);
writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
mMethodMap);
}
@@ -3113,8 +3143,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
final String imeSubtypeExtraValue =
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
- final boolean isAuxiliary =
- Boolean.valueOf(parser.getAttributeValue(null, ATTR_IS_AUXILIARY));
+ final boolean isAuxiliary = "1".equals(String.valueOf(
+ parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
final InputMethodSubtype subtype =
new InputMethodSubtype(label, icon, imeSubtypeLocale,
imeSubtypeMode, imeSubtypeExtraValue, isAuxiliary);
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 1d3e3ac..b3d7220 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -41,7 +41,7 @@ import android.content.IntentFilter;
/**
* {@hide}
*/
-public class IntentResolver<F extends IntentFilter, R extends Object> {
+public abstract 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 || false;
@@ -333,14 +333,19 @@ public class IntentResolver<F extends IntentFilter, R extends Object> {
return false;
}
- protected String packageForFilter(F filter) {
- return null;
- }
+ /**
+ * Return the package that owns this filter. This must be implemented to
+ * provide correct filtering of Intents that have specified a package name
+ * they are to be delivered to.
+ */
+ protected abstract String packageForFilter(F filter);
+ @SuppressWarnings("unchecked")
protected R newResult(F filter, int match) {
return (R)filter;
}
+ @SuppressWarnings("unchecked")
protected void sortResults(List<R> results) {
Collections.sort(results, mResolvePrioritySorter);
}
@@ -502,6 +507,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> {
String resolvedType, String scheme, List<F> src, List<R> dest) {
final String action = intent.getAction();
final Uri data = intent.getData();
+ final String packageName = intent.getPackage();
final boolean excludingStopped = intent.isExcludingStopped();
@@ -520,6 +526,14 @@ public class IntentResolver<F extends IntentFilter, R extends Object> {
continue;
}
+ // Is delivery being limited to filters owned by a particular package?
+ if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (debug) {
+ Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
+ }
+ continue;
+ }
+
// Do we already have this one?
if (!allowFilterResult(filter, dest)) {
if (debug) {
@@ -561,6 +575,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> {
}
// Sorts a List of IntentFilter objects into descending priority order.
+ @SuppressWarnings("rawtypes")
private static final Comparator mResolvePrioritySorter = new Comparator() {
public int compare(Object o1, Object o2) {
final int q1 = ((IntentFilter) o1).getPriority();
diff --git a/services/java/com/android/server/LoadAverageService.java b/services/java/com/android/server/LoadAverageService.java
index b6baadb..da9fc99 100644
--- a/services/java/com/android/server/LoadAverageService.java
+++ b/services/java/com/android/server/LoadAverageService.java
@@ -278,14 +278,14 @@ public class LoadAverageService extends Service {
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.TOP;
params.setTitle("Load Average");
- WindowManagerImpl wm = (WindowManagerImpl)getSystemService(WINDOW_SERVICE);
+ WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);
wm.addView(mView, params);
}
@Override
public void onDestroy() {
super.onDestroy();
- ((WindowManagerImpl)getSystemService(WINDOW_SERVICE)).removeView(mView);
+ ((WindowManager)getSystemService(WINDOW_SERVICE)).removeView(mView);
mView = null;
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 656ec4d..56afe7f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -195,6 +195,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
final Object mKey;
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
int mPendingBroadcasts;
+ String requiredPermissions;
Receiver(ILocationListener listener) {
mListener = listener;
@@ -284,7 +285,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
+ mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
+ requiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -319,7 +321,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
+ mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
+ requiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -358,7 +361,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
+ mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
+ requiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -572,22 +576,30 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return Settings.Secure.isLocationProviderEnabled(resolver, provider);
}
- private void checkPermissionsSafe(String provider) {
- if ((LocationManager.GPS_PROVIDER.equals(provider)
- || LocationManager.PASSIVE_PROVIDER.equals(provider))
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION permission");
+ private String checkPermissionsSafe(String provider, String lastPermission) {
+ if (LocationManager.GPS_PROVIDER.equals(provider)
+ || LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+ if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Provider " + provider
+ + " requires ACCESS_FINE_LOCATION permission");
+ }
+ return ACCESS_FINE_LOCATION;
}
- if (LocationManager.NETWORK_PROVIDER.equals(provider)
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)
- && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
+
+ // Assume any other provider requires the coarse or fine permission.
+ if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return ACCESS_FINE_LOCATION.equals(lastPermission)
+ ? lastPermission : ACCESS_COARSE_LOCATION;
}
+ if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return ACCESS_FINE_LOCATION;
+ }
+
+ throw new SecurityException("Provider " + provider
+ + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
}
private boolean isAllowedProviderSafe(String provider) {
@@ -1099,8 +1111,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ void validatePendingIntent(PendingIntent intent) {
+ if (intent.isTargetedToPackage()) {
+ return;
+ }
+ Slog.i(TAG, "Given Intent does not require a specific package: "
+ + intent);
+ // XXX we should really throw a security exception, if the caller's
+ // targetSdkVersion is high enough.
+ //throw new SecurityException("Given Intent does not require a specific package: "
+ // + intent);
+ }
+
public void requestLocationUpdatesPI(String provider, Criteria criteria,
long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
+ validatePendingIntent(intent);
if (criteria != null) {
// FIXME - should we consider using multiple providers simultaneously
// rather than only the best one?
@@ -1132,7 +1157,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new IllegalArgumentException("provider=" + provider);
}
- checkPermissionsSafe(provider);
+ receiver.requiredPermissions = checkPermissionsSafe(provider,
+ receiver.requiredPermissions);
// so wakelock calls will succeed
final int callingUid = Binder.getCallingUid();
@@ -1300,7 +1326,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
// first check for permission to the provider
- checkPermissionsSafe(provider);
+ checkPermissionsSafe(provider, null);
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -1432,7 +1458,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (this) {
// synchronize to ensure incrementPendingBroadcasts()
// is called before decrementPendingBroadcasts()
- intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
+ intent.send(mContext, 0, enteredIntent, this, mLocationHandler,
+ ACCESS_FINE_LOCATION);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcasts();
@@ -1457,7 +1484,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (this) {
// synchronize to ensure incrementPendingBroadcasts()
// is called before decrementPendingBroadcasts()
- intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
+ intent.send(mContext, 0, exitedIntent, this, mLocationHandler,
+ ACCESS_FINE_LOCATION);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcasts();
@@ -1526,6 +1554,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
public void addProximityAlert(double latitude, double longitude,
float radius, long expiration, PendingIntent intent) {
+ validatePendingIntent(intent);
try {
synchronized (mLock) {
addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
@@ -1626,7 +1655,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return null;
}
- checkPermissionsSafe(provider);
+ checkPermissionsSafe(provider, null);
Bundle b = new Bundle();
b.putBoolean("network", p.requiresNetwork());
@@ -1668,7 +1697,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
private boolean _isProviderEnabledLocked(String provider) {
- checkPermissionsSafe(provider);
+ checkPermissionsSafe(provider, null);
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
@@ -1694,7 +1723,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
private Location _getLastKnownLocationLocked(String provider) {
- checkPermissionsSafe(provider);
+ checkPermissionsSafe(provider, null);
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d3244ec..2e54c99 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1075,18 +1075,22 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
com.android.internal.R.styleable.Storage_mtpReserve, 0);
boolean allowMassStorage = a.getBoolean(
com.android.internal.R.styleable.Storage_allowMassStorage, false);
+ // resource parser does not support longs, so XML value is in megabytes
+ long maxFileSize = a.getInt(
+ com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
Slog.d(TAG, "got storage path: " + path + " description: " + description +
" primary: " + primary + " removable: " + removable +
" emulated: " + emulated + " mtpReserve: " + mtpReserve +
- " allowMassStorage: " + allowMassStorage);
+ " allowMassStorage: " + allowMassStorage +
+ " maxFileSize: " + maxFileSize);
if (path == null || description == null) {
Slog.e(TAG, "path or description is null in readStorageList");
} else {
String pathString = path.toString();
StorageVolume volume = new StorageVolume(pathString,
description.toString(), removable, emulated,
- mtpReserve, allowMassStorage);
+ mtpReserve, allowMassStorage, maxFileSize);
if (primary) {
if (mPrimaryVolume == null) {
mPrimaryVolume = volume;
@@ -1626,6 +1630,30 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
}
}
+ public String getSecureContainerFilesystemPath(String id) {
+ validatePermission(android.Manifest.permission.ASEC_ACCESS);
+ waitForReady();
+ warnOnNotMounted();
+
+ try {
+ ArrayList<String> rsp = mConnector.doCommand(String.format("asec fspath %s", id));
+ String []tok = rsp.get(0).split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code != VoldResponseCode.AsecPathResult) {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ return tok[1];
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageNotFound) {
+ Slog.i(TAG, String.format("Container '%s' not found", id));
+ return null;
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ }
+
public void finishMediaUpdate() {
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 2b01c5e..da1bf83 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,10 +16,11 @@
package com.android.server;
+import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.provider.Settings.Secure.NETSTATS_ENABLED;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -35,8 +36,14 @@ import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
import java.io.BufferedReader;
import java.io.DataInputStream;
@@ -47,7 +54,10 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
@@ -65,9 +75,18 @@ class NetworkManagementService extends INetworkManagementService.Stub {
private static final int ADD = 1;
private static final int REMOVE = 2;
+ /** Path to {@code /proc/uid_stat}. */
@Deprecated
- private static final File STATS_UIDSTAT = new File("/proc/uid_stat");
- private static final File STATS_NETFILTER = new File("/proc/net/xt_qtaguid/stats");
+ private final File mProcStatsUidstat;
+ /** Path to {@code /proc/net/xt_qtaguid/stats}. */
+ private final File mProcStatsNetfilter;
+
+ /** {@link #mProcStatsNetfilter} headers. */
+ private static final String KEY_IFACE = "iface";
+ private static final String KEY_TAG_HEX = "acct_tag_hex";
+ private static final String KEY_UID = "uid_tag_int";
+ private static final String KEY_RX = "rx_bytes";
+ private static final String KEY_TX = "tx_bytes";
class NetdResponseCode {
public static final int InterfaceListResult = 110;
@@ -102,15 +121,25 @@ class NetworkManagementService extends INetworkManagementService.Stub {
private ArrayList<INetworkManagementEventObserver> mObservers;
+ /** Set of interfaces with active quotas. */
+ private HashSet<String> mInterfaceQuota = Sets.newHashSet();
+ /** Set of UIDs with active reject rules. */
+ private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
+
+ private boolean mBandwidthControlEnabled;
+
/**
* Constructs a new NetworkManagementService instance
*
* @param context Binder context for this service
*/
- private NetworkManagementService(Context context) {
+ private NetworkManagementService(Context context, File procRoot) {
mContext = context;
mObservers = new ArrayList<INetworkManagementEventObserver>();
+ mProcStatsUidstat = new File(procRoot, "uid_stat");
+ mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats");
+
if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
return;
}
@@ -121,7 +150,8 @@ class NetworkManagementService extends INetworkManagementService.Stub {
}
public static NetworkManagementService create(Context context) throws InterruptedException {
- NetworkManagementService service = new NetworkManagementService(context);
+ NetworkManagementService service = new NetworkManagementService(
+ context, new File("/proc/"));
if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
service.mThread.start();
if (DBG) Slog.d(TAG, "Awaiting socket connection");
@@ -130,6 +160,35 @@ class NetworkManagementService extends INetworkManagementService.Stub {
return service;
}
+ // @VisibleForTesting
+ public static NetworkManagementService createForTest(Context context, File procRoot) {
+ // TODO: eventually connect with mock netd
+ return new NetworkManagementService(context, procRoot);
+ }
+
+ public void systemReady() {
+
+ // only enable bandwidth control when support exists, and requested by
+ // system setting.
+ // TODO: eventually migrate to be always enabled
+ final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
+ final boolean shouldEnable =
+ Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 0) != 0;
+
+ mBandwidthControlEnabled = false;
+ if (hasKernelSupport && shouldEnable) {
+ Slog.d(TAG, "enabling bandwidth control");
+ try {
+ mConnector.doCommand("bandwidth enable");
+ mBandwidthControlEnabled = true;
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "problem enabling bandwidth controls", e);
+ }
+ } else {
+ Slog.d(TAG, "not enabling bandwidth control");
+ }
+ }
+
public void registerObserver(INetworkManagementEventObserver obs) {
Slog.d(TAG, "Registering observer");
mObservers.add(obs);
@@ -141,12 +200,26 @@ class NetworkManagementService extends INetworkManagementService.Stub {
}
/**
- * Notify our observers of an interface link status change
+ * Notify our observers of an interface status change
+ */
+ private void notifyInterfaceStatusChanged(String iface, boolean up) {
+ for (INetworkManagementEventObserver obs : mObservers) {
+ try {
+ obs.interfaceStatusChanged(iface, up);
+ } catch (Exception ex) {
+ Slog.w(TAG, "Observer notifier failed", ex);
+ }
+ }
+ }
+
+ /**
+ * Notify our observers of an interface link state change
+ * (typically, an Ethernet cable has been plugged-in or unplugged).
*/
- private void notifyInterfaceLinkStatusChanged(String iface, boolean link) {
+ private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
for (INetworkManagementEventObserver obs : mObservers) {
try {
- obs.interfaceLinkStatusChanged(iface, link);
+ obs.interfaceLinkStateChanged(iface, up);
} catch (Exception ex) {
Slog.w(TAG, "Observer notifier failed", ex);
}
@@ -207,6 +280,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
* Format: "NNN Iface added <name>"
* "NNN Iface removed <name>"
* "NNN Iface changed <name> <up/down>"
+ * "NNN Iface linkstatus <name> <up/down>"
*/
if (cooked.length < 4 || !cooked[1].equals("Iface")) {
throw new IllegalStateException(
@@ -219,7 +293,10 @@ class NetworkManagementService extends INetworkManagementService.Stub {
notifyInterfaceRemoved(cooked[3]);
return true;
} else if (cooked[2].equals("changed") && cooked.length == 5) {
- notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up"));
+ notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
+ return true;
+ } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
+ notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
return true;
}
throw new IllegalStateException(
@@ -870,7 +947,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- if (STATS_NETFILTER.exists()) {
+ if (mBandwidthControlEnabled) {
return getNetworkStatsDetailNetfilter(UID_ALL);
} else {
return getNetworkStatsDetailUidstat(UID_ALL);
@@ -878,13 +955,103 @@ class NetworkManagementService extends INetworkManagementService.Stub {
}
@Override
+ public void setInterfaceQuota(String iface, long quota) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ // silently discard when control disabled
+ // TODO: eventually migrate to be always enabled
+ if (!mBandwidthControlEnabled) return;
+
+ synchronized (mInterfaceQuota) {
+ if (mInterfaceQuota.contains(iface)) {
+ // TODO: eventually consider throwing
+ return;
+ }
+
+ final StringBuilder command = new StringBuilder();
+ command.append("bandwidth setiquota ").append(iface).append(" ").append(quota);
+
+ try {
+ // TODO: add support for quota shared across interfaces
+ mConnector.doCommand(command.toString());
+ mInterfaceQuota.add(iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
+ }
+ }
+
+ @Override
+ public void removeInterfaceQuota(String iface) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ // silently discard when control disabled
+ // TODO: eventually migrate to be always enabled
+ if (!mBandwidthControlEnabled) return;
+
+ synchronized (mInterfaceQuota) {
+ if (!mInterfaceQuota.contains(iface)) {
+ // TODO: eventually consider throwing
+ return;
+ }
+
+ final StringBuilder command = new StringBuilder();
+ command.append("bandwidth removeiquota ").append(iface);
+
+ try {
+ // TODO: add support for quota shared across interfaces
+ mConnector.doCommand(command.toString());
+ mInterfaceQuota.remove(iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
+ }
+ }
+
+ @Override
+ public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ // silently discard when control disabled
+ // TODO: eventually migrate to be always enabled
+ if (!mBandwidthControlEnabled) return;
+
+ synchronized (mUidRejectOnQuota) {
+ final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
+ if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
+ // TODO: eventually consider throwing
+ return;
+ }
+
+ final StringBuilder command = new StringBuilder();
+ command.append("bandwidth");
+ if (rejectOnQuotaInterfaces) {
+ command.append(" addnaughtyapps");
+ } else {
+ command.append(" removenaughtyapps");
+ }
+ command.append(" ").append(uid);
+
+ try {
+ mConnector.doCommand(command.toString());
+ if (rejectOnQuotaInterfaces) {
+ mUidRejectOnQuota.put(uid, true);
+ } else {
+ mUidRejectOnQuota.delete(uid);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
+ }
+ }
+
public NetworkStats getNetworkStatsUidDetail(int uid) {
if (Binder.getCallingUid() != uid) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
}
- if (STATS_NETFILTER.exists()) {
+ if (mBandwidthControlEnabled) {
return getNetworkStatsDetailNetfilter(uid);
} else {
return getNetworkStatsDetailUidstat(uid);
@@ -896,35 +1063,36 @@ class NetworkManagementService extends INetworkManagementService.Stub {
*/
private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
+ final ArrayList<String> keys = Lists.newArrayList();
+ final ArrayList<String> values = Lists.newArrayList();
+ final HashMap<String, String> parsed = Maps.newHashMap();
BufferedReader reader = null;
try {
- reader = new BufferedReader(new FileReader(STATS_NETFILTER));
-
- // assumes format from kernel:
- // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes
+ reader = new BufferedReader(new FileReader(mProcStatsNetfilter));
- // skip first line, which is legend
+ // parse first line as header
String line = reader.readLine();
- while ((line = reader.readLine()) != null) {
- final StringTokenizer t = new StringTokenizer(line);
+ splitLine(line, keys);
- final String idx = t.nextToken();
- final String iface = t.nextToken();
+ // parse remaining lines
+ while ((line = reader.readLine()) != null) {
+ splitLine(line, values);
+ parseLine(keys, values, parsed);
try {
- // TODO: kernel currently emits tag in upper half of long;
- // eventually switch to directly using int.
- final int tag = (int) (Long.parseLong(t.nextToken().substring(2), 16) >> 32);
- final int uid = Integer.parseInt(t.nextToken());
- final long rx = Long.parseLong(t.nextToken());
- final long tx = Long.parseLong(t.nextToken());
+ final String iface = parsed.get(KEY_IFACE);
+ final int tag = NetworkManagementSocketTagger.kernelToTag(
+ parsed.get(KEY_TAG_HEX));
+ final int uid = Integer.parseInt(parsed.get(KEY_UID));
+ final long rx = Long.parseLong(parsed.get(KEY_RX));
+ final long tx = Long.parseLong(parsed.get(KEY_TX));
if (limitUid == UID_ALL || limitUid == uid) {
stats.addEntry(iface, uid, tag, rx, tx);
}
} catch (NumberFormatException e) {
- Slog.w(TAG, "problem parsing stats for idx " + idx + ": " + e);
+ Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
}
}
} catch (IOException e) {
@@ -946,7 +1114,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
final String[] knownUids;
if (limitUid == UID_ALL) {
- knownUids = STATS_UIDSTAT.list();
+ knownUids = mProcStatsUidstat.list();
} else {
knownUids = new String[] { String.valueOf(limitUid) };
}
@@ -955,7 +1123,7 @@ class NetworkManagementService extends INetworkManagementService.Stub {
SystemClock.elapsedRealtime(), knownUids.length);
for (String uid : knownUids) {
final int uidInt = Integer.parseInt(uid);
- final File uidPath = new File(STATS_UIDSTAT, uid);
+ final File uidPath = new File(mProcStatsUidstat, uid);
final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx);
@@ -1023,10 +1191,30 @@ class NetworkManagementService extends INetworkManagementService.Stub {
return getInterfaceThrottle(iface, false);
}
- @Override
- public void setBandwidthControlEnabled(boolean enabled) {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- mConnector.doCommand(String.format("bandwidth %s", (enabled ? "enable" : "disable")));
+ /**
+ * Split given line into {@link ArrayList}.
+ */
+ private static void splitLine(String line, ArrayList<String> outSplit) {
+ outSplit.clear();
+
+ final StringTokenizer t = new StringTokenizer(line);
+ while (t.hasMoreTokens()) {
+ outSplit.add(t.nextToken());
+ }
+ }
+
+ /**
+ * Zip the two given {@link ArrayList} as key and value pairs into
+ * {@link HashMap}.
+ */
+ private static void parseLine(
+ ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
+ outParsed.clear();
+
+ final int size = Math.min(keys.size(), values.size());
+ for (int i = 0; i < size; i++) {
+ outParsed.put(keys.get(i), values.get(i));
+ }
}
/**
@@ -1043,4 +1231,65 @@ class NetworkManagementService extends INetworkManagementService.Stub {
return -1;
}
}
+
+ public void setDefaultInterfaceForDns(String iface) throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ try {
+ String cmd = "resolver setdefaultif " + iface;
+
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating with native daemon to set default interface", e);
+ }
+ }
+
+ public void setDnsServersForInterface(String iface, String[] servers)
+ throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
+ "NetworkManagementService");
+ try {
+ String cmd = "resolver setifdns " + iface;
+ for (String s : servers) {
+ if (s != null && !"0.0.0.0".equals(s) &&
+ !"::".equals(s) && !"0:0:0:0:0:0:0:0".equals(s)) {
+ cmd += " " + InetAddress.getByName(s).getHostAddress();
+ }
+ }
+
+ mConnector.doCommand(cmd);
+ } catch (UnknownHostException e) {
+ throw new IllegalStateException("failed to resolve dns address.", e);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating with native deamon to set dns for interface", e);
+ }
+ }
+
+ public void flushDefaultDnsCache() throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ try {
+ String cmd = "resolver flushdefaultif";
+
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating with native deamon to flush default interface", e);
+ }
+ }
+
+ public void flushInterfaceDnsCache(String iface) throws IllegalStateException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ try {
+ String cmd = "resolver flushif " + iface;
+
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating with native deamon to flush interface " + iface, e);
+ }
+ }
}
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index 15f22c0..f7fe39e 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -16,8 +16,6 @@
package com.android.server;
-import com.android.internal.telephony.TelephonyIntents;
-
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -28,7 +26,6 @@ import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.net.SntpClient;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -36,12 +33,10 @@ import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
-import android.util.Slog;
+import android.util.NtpTrustedTime;
+import android.util.TrustedTime;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.Properties;
+import com.android.internal.telephony.TelephonyIntents;
/**
* Monitors the network time and updates the system time if it is out of sync
@@ -68,14 +63,11 @@ public class NetworkTimeUpdateService {
private static final long POLLING_INTERVAL_SHORTER_MS = 60 * 1000L; // 60 seconds
/** Number of times to try again */
private static final int TRY_AGAIN_TIMES_MAX = 3;
- /** How long to wait for the NTP server to respond. */
- private static final int MAX_NTP_FETCH_WAIT_MS = 20 * 1000;
/** If the time difference is greater than this threshold, then update the time. */
private static final int TIME_ERROR_THRESHOLD_MS = 5 * 1000;
private static final String ACTION_POLL =
"com.android.server.NetworkTimeUpdateService.action.POLL";
- private static final String PROPERTIES_FILE = "/etc/gps.conf";
private static int POLL_REQUEST = 0;
private static final long NOT_SET = -1;
@@ -84,14 +76,14 @@ public class NetworkTimeUpdateService {
private long mNitzZoneSetTime = NOT_SET;
private Context mContext;
+ private TrustedTime mTime;
+
// NTP lookup is done on this thread and handler
private Handler mHandler;
private HandlerThread mThread;
private AlarmManager mAlarmManager;
private PendingIntent mPendingPollIntent;
private SettingsObserver mSettingsObserver;
- // Address of the NTP server
- private String mNtpServer;
// The last time that we successfully fetched the NTP time.
private long mLastNtpFetchTime = NOT_SET;
// Keeps track of how many quick attempts were made to fetch NTP time.
@@ -101,6 +93,7 @@ public class NetworkTimeUpdateService {
public NetworkTimeUpdateService(Context context) {
mContext = context;
+ mTime = NtpTrustedTime.getInstance(context);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
Intent pollIntent = new Intent(ACTION_POLL, null);
mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
@@ -108,12 +101,6 @@ public class NetworkTimeUpdateService {
/** Initialize the receivers and initiate the first NTP request */
public void systemReady() {
- mNtpServer = getNtpServerAddress();
- if (mNtpServer == null) {
- Slog.e(TAG, "NTP server address not found, not syncing to NTP time");
- return;
- }
-
registerForTelephonyIntents();
registerForAlarms();
registerForConnectivityIntents();
@@ -128,27 +115,6 @@ public class NetworkTimeUpdateService {
mSettingsObserver.observe(mContext);
}
- private String getNtpServerAddress() {
- String serverAddress = null;
- FileInputStream stream = null;
- try {
- Properties properties = new Properties();
- File file = new File(PROPERTIES_FILE);
- stream = new FileInputStream(file);
- properties.load(stream);
- serverAddress = properties.getProperty("NTP_SERVER", null);
- } catch (IOException e) {
- Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (Exception e) {}
- }
- }
- return serverAddress;
- }
-
private void registerForTelephonyIntents() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
@@ -189,9 +155,15 @@ public class NetworkTimeUpdateService {
if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + POLLING_INTERVAL_MS
|| event == EVENT_AUTO_TIME_CHANGED) {
if (DBG) Log.d(TAG, "Before Ntp fetch");
- long ntp = getNtpTime();
- if (DBG) Log.d(TAG, "Ntp = " + ntp);
- if (ntp > 0) {
+
+ // force refresh NTP cache when outdated
+ if (mTime.getCacheAge() >= POLLING_INTERVAL_MS) {
+ mTime.forceRefresh();
+ }
+
+ // only update when NTP time is fresh
+ if (mTime.getCacheAge() < POLLING_INTERVAL_MS) {
+ final long ntp = mTime.currentTimeMillis();
mTryAgainCounter = 0;
mLastNtpFetchTime = SystemClock.elapsedRealtime();
if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS) {
@@ -232,15 +204,6 @@ public class NetworkTimeUpdateService {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
}
- private long getNtpTime() {
- SntpClient client = new SntpClient();
- if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT_MS)) {
- return client.getNtpTime();
- } else {
- return 0;
- }
- }
-
/**
* Checks if the user prefers to automatically set the time.
*/
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 08c6699..950f3b6 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -138,7 +138,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
if (outFile == null) {
Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]");
}
- FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
+ FullBackup.restoreFile(data, size, type, mode, mtime, outFile);
if (restoredWallpaper) {
WallpaperManagerService wallpaper =
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a23bacf..8c7e279 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -209,9 +209,11 @@ class ServerThread extends Thread {
bluetoothA2dp);
bluetooth.initAfterA2dpRegistration();
+ int airplaneModeOn = Settings.System.getInt(mContentResolver,
+ Settings.System.AIRPLANE_MODE_ON, 0);
int bluetoothOn = Settings.Secure.getInt(mContentResolver,
Settings.Secure.BLUETOOTH_ON, 0);
- if (bluetoothOn > 0) {
+ if (airplaneModeOn == 0 && bluetoothOn != 0) {
bluetooth.enable();
}
}
@@ -281,7 +283,8 @@ class ServerThread extends Thread {
try {
Slog.i(TAG, "NetworkPolicy Service");
networkPolicy = new NetworkPolicyManagerService(
- context, ActivityManagerService.self(), power, networkStats);
+ context, ActivityManagerService.self(), power,
+ networkStats, networkManagement);
ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting NetworkPolicy Service", e);
@@ -520,6 +523,7 @@ class ServerThread extends Thread {
// These are needed to propagate to the runnable below.
final Context contextF = context;
final BatteryService batteryF = battery;
+ final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
@@ -547,6 +551,7 @@ class ServerThread extends Thread {
startSystemUi(contextF);
if (batteryF != null) batteryF.systemReady();
+ if (networkManagementF != null) networkManagementF.systemReady();
if (networkStatsF != null) networkStatsF.systemReady();
if (networkPolicyF != null) networkPolicyF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index 7266d7d..24d4dd3 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -16,9 +16,6 @@
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;
@@ -54,6 +51,9 @@ import android.util.NtpTrustedTime;
import android.util.Slog;
import android.util.TrustedTime;
+import com.android.internal.R;
+import com.android.internal.telephony.TelephonyProperties;
+
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileDescriptor;
@@ -63,7 +63,6 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.GregorianCalendar;
-import java.util.Properties;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -87,7 +86,6 @@ public class ThrottleService extends IThrottleManager.Stub {
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;
@@ -127,8 +125,6 @@ public class ThrottleService extends IThrottleManager.Stub {
private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
- private static final String PROPERTIES_FILE = "/etc/gps.conf";
-
private Intent mPollStickyBroadcast;
private TrustedTime mTime;
@@ -139,8 +135,7 @@ public class ThrottleService extends IThrottleManager.Stub {
}
public ThrottleService(Context context) {
- // TODO: move to using cached NtpTrustedTime
- this(context, getNetworkManagementService(), new NtpTrustedTime(),
+ this(context, getNetworkManagementService(), NtpTrustedTime.getInstance(context),
context.getResources().getString(R.string.config_datause_iface));
}
@@ -179,14 +174,17 @@ public class ThrottleService extends IThrottleManager.Stub {
mIface = iface;
}
- public void interfaceLinkStatusChanged(String iface, boolean link) {
- if (link) {
+ public void interfaceStatusChanged(String iface, boolean up) {
+ if (up) {
if (TextUtils.equals(iface, mIface)) {
mHandler.obtainMessage(mMsg).sendToTarget();
}
}
}
+ public void interfaceLinkStateChanged(String iface, boolean up) {
+ }
+
public void interfaceAdded(String iface) {
// TODO - an interface added in the UP state should also trigger a StatusChanged
// notification..
@@ -338,26 +336,6 @@ public class ThrottleService extends IThrottleManager.Stub {
}
}, new IntentFilter(ACTION_RESET));
- FileInputStream stream = null;
- try {
- Properties properties = new Properties();
- File file = new File(PROPERTIES_FILE);
- stream = new FileInputStream(file);
- properties.load(stream);
- 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 {
- if (stream != null) {
- try {
- stream.close();
- } catch (Exception e) {}
- }
- }
-
// use a new thread as we don't want to stall the system for file writes
mThread = new HandlerThread(TAG);
mThread.start();
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index cb55451..5f0922e 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -36,6 +36,7 @@ import android.net.wifi.WifiManager;
import android.net.wifi.WifiStateMachine;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiWatchdogService;
import android.net.wifi.WpsConfiguration;
import android.net.wifi.WpsResult;
import android.net.ConnectivityManager;
@@ -342,6 +343,7 @@ public class WifiService extends IWifiManager.Stub {
* Protected by mWifiStateTracker lock.
*/
private final WorkSource mTmpWorkSource = new WorkSource();
+ private WifiWatchdogService mWifiWatchdogService;
WifiService(Context context) {
mContext = context;
@@ -431,6 +433,9 @@ public class WifiService extends IWifiManager.Stub {
Slog.i(TAG, "WifiService starting up with Wi-Fi " +
(wifiEnabled ? "enabled" : "disabled"));
setWifiEnabled(wifiEnabled);
+
+ //TODO: as part of WWS refactor, create only when needed
+ mWifiWatchdogService = new WifiWatchdogService(mContext);
}
private boolean testAndClearWifiSavedState() {
@@ -1155,6 +1160,10 @@ public class WifiService extends IWifiManager.Stub {
pw.println();
pw.println("Locks held:");
mLocks.dump(pw);
+
+ pw.println();
+ pw.println("WifiWatchdogService dump");
+ mWifiWatchdogService.dump(pw);
}
private class WifiLock extends DeathRecipient {
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
deleted file mode 100644
index 56bfbe0..0000000
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ /dev/null
@@ -1,1450 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.NetworkInfo;
-import android.net.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.Slog;
-
-import java.io.BufferedInputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-import java.net.URL;
-import java.util.Collection;
-import java.util.List;
-import java.util.Random;
-import java.util.Scanner;
-
-/**
- * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi
- * network with multiple access points. After the framework successfully
- * connects to an access point, the watchdog verifies whether the DNS server is
- * reachable. If not, the watchdog blacklists the current access point, leading
- * to a connection on another access point within the same network.
- * <p>
- * The watchdog has a few safeguards:
- * <ul>
- * <li>Only monitor networks with multiple access points
- * <li>Only check at most {@link #getMaxApChecks()} different access points
- * within the network before giving up
- * <p>
- * The watchdog checks for connectivity on an access point by ICMP pinging the
- * DNS. There are settings that allow disabling the watchdog, or tweaking the
- * acceptable packet loss (and other various parameters).
- * <p>
- * The core logic of the watchdog is done on the main watchdog thread. Wi-Fi
- * callbacks can come in on other threads, so we must queue messages to the main
- * watchdog thread's handler. Most (if not all) state is only written to from
- * the main thread.
- *
- * {@hide}
- */
-public class WifiWatchdogService {
- private static final String TAG = "WifiWatchdogService";
- private static final boolean V = false;
- private static final boolean D = true;
-
- private Context mContext;
- private ContentResolver mContentResolver;
- private WifiManager mWifiManager;
- private ConnectivityManager mConnectivityManager;
-
- /**
- * The main watchdog thread.
- */
- private WifiWatchdogThread mThread;
- /**
- * The handler for the main watchdog thread.
- */
- private WifiWatchdogHandler mHandler;
-
- private ContentObserver mContentObserver;
-
- /**
- * The current watchdog state. Only written from the main thread!
- */
- private WatchdogState mState = WatchdogState.IDLE;
- /**
- * The SSID of the network that the watchdog is currently monitoring. Only
- * touched in the main thread!
- */
- private String mSsid;
- /**
- * The number of access points in the current network ({@link #mSsid}) that
- * have been checked. Only touched in the main thread, using getter/setter methods.
- */
- private int mBssidCheckCount;
- /** Whether the current AP check should be canceled. */
- private boolean mShouldCancel;
-
- WifiWatchdogService(Context context) {
- mContext = context;
- mContentResolver = context.getContentResolver();
- mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- createThread();
-
- // The content observer to listen needs a handler, which createThread creates
- registerForSettingsChanges();
- if (isWatchdogEnabled()) {
- registerForWifiBroadcasts();
- }
-
- if (V) {
- myLogV("WifiWatchdogService: Created");
- }
- }
-
- /**
- * Observes the watchdog on/off setting, and takes action when changed.
- */
- private void registerForSettingsChanges() {
- ContentResolver contentResolver = mContext.getContentResolver();
- contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), false,
- mContentObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- if (isWatchdogEnabled()) {
- registerForWifiBroadcasts();
- } else {
- unregisterForWifiBroadcasts();
- if (mHandler != null) {
- mHandler.disableWatchdog();
- }
- }
- }
- });
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON
- */
- private boolean isWatchdogEnabled() {
- return Settings.Secure.getInt(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1;
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_AP_COUNT
- */
- private int getApCount() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_AP_COUNT, 2);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT
- */
- private int getInitialIgnoredPingCount() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT , 2);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_COUNT
- */
- private int getPingCount() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_PING_COUNT, 4);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_TIMEOUT_MS
- */
- private int getPingTimeoutMs() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS, 500);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_DELAY_MS
- */
- private int getPingDelayMs() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, 250);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED
- */
- private Boolean isWalledGardenTestEnabled() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1;
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL
- */
- private String getWalledGardenUrl() {
- String url = Settings.Secure.getString(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL);
- if (TextUtils.isEmpty(url)) return "http://www.google.com/";
- return url;
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN
- */
- private String getWalledGardenPattern() {
- String pattern = Settings.Secure.getString(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN);
- if (TextUtils.isEmpty(pattern)) return "<title>.*Google.*</title>";
- return pattern;
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE
- */
- private int getAcceptablePacketLossPercentage() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE, 25);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS
- */
- private int getMaxApChecks() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_MAX_AP_CHECKS, 7);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED
- */
- private boolean isBackgroundCheckEnabled() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED, 1) == 1;
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS
- */
- private int getBackgroundCheckDelayMs() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS, 60000);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS
- */
- private int getBackgroundCheckTimeoutMs() {
- return Settings.Secure.getInt(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS, 1000);
- }
-
- /**
- * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WATCH_LIST
- * @return the comma-separated list of SSIDs
- */
- private String getWatchList() {
- return Settings.Secure.getString(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_WATCH_LIST);
- }
-
- /**
- * Registers to receive the necessary Wi-Fi broadcasts.
- */
- private void registerForWifiBroadcasts() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- mContext.registerReceiver(mReceiver, intentFilter);
- }
-
- /**
- * Unregisters from receiving the Wi-Fi broadcasts.
- */
- private void unregisterForWifiBroadcasts() {
- mContext.unregisterReceiver(mReceiver);
- }
-
- /**
- * Creates the main watchdog thread, including waiting for the handler to be
- * created.
- */
- private void createThread() {
- mThread = new WifiWatchdogThread();
- mThread.start();
- waitForHandlerCreation();
- }
-
- /**
- * Unregister broadcasts and quit the watchdog thread
- */
- //TODO: Change back to running WWS when needed
-// private void quit() {
-// unregisterForWifiBroadcasts();
-// mContext.getContentResolver().unregisterContentObserver(mContentObserver);
-// mHandler.removeAllActions();
-// mHandler.getLooper().quit();
-// }
-
- /**
- * Waits for the main watchdog thread to create the handler.
- */
- private void waitForHandlerCreation() {
- synchronized(this) {
- while (mHandler == null) {
- try {
- // Wait for the handler to be set by the other thread
- wait();
- } catch (InterruptedException e) {
- Slog.e(TAG, "Interrupted while waiting on handler.");
- }
- }
- }
- }
-
- // Utility methods
-
- /**
- * Logs with the current thread.
- */
- private static void myLogV(String message) {
- Slog.v(TAG, "(" + Thread.currentThread().getName() + ") " + message);
- }
-
- private static void myLogD(String message) {
- Slog.d(TAG, "(" + Thread.currentThread().getName() + ") " + message);
- }
-
- /**
- * Gets the first DNS of the current AP.
- *
- * @return The first DNS of the current AP.
- */
- private InetAddress getDns() {
- if (mConnectivityManager == null) {
- mConnectivityManager = (ConnectivityManager)mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
-
- LinkProperties linkProperties = mConnectivityManager.getLinkProperties(
- ConnectivityManager.TYPE_WIFI);
- if (linkProperties == null) return null;
-
- Collection<InetAddress> dnses = linkProperties.getDnses();
- if (dnses == null || dnses.size() == 0) return null;
-
- return dnses.iterator().next();
- }
-
- /**
- * Checks whether the DNS can be reached using multiple attempts according
- * to the current setting values.
- *
- * @return Whether the DNS is reachable
- */
- private boolean checkDnsConnectivity() {
- InetAddress dns = getDns();
- if (dns == null) {
- if (V) {
- myLogV("checkDnsConnectivity: Invalid DNS, returning false");
- }
- return false;
- }
-
- if (V) {
- myLogV("checkDnsConnectivity: Checking " + dns.getHostAddress() + " for connectivity");
- }
-
- int numInitialIgnoredPings = getInitialIgnoredPingCount();
- int numPings = getPingCount();
- int pingDelay = getPingDelayMs();
- int acceptableLoss = getAcceptablePacketLossPercentage();
-
- /** See {@link Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} */
- int ignoredPingCounter = 0;
- int pingCounter = 0;
- int successCounter = 0;
-
- // No connectivity check needed
- if (numPings == 0) {
- return true;
- }
-
- // Do the initial pings that we ignore
- for (; ignoredPingCounter < numInitialIgnoredPings; ignoredPingCounter++) {
- if (shouldCancel()) return false;
-
- boolean dnsAlive = DnsPinger.isDnsReachable(dns, getPingTimeoutMs());
- if (dnsAlive) {
- /*
- * Successful "ignored" pings are *not* ignored (they count in the total number
- * of pings), but failures are really ignored.
- */
-
- // TODO: This is confusing logic and should be rewitten
- // Here, successful 'ignored' pings are interpreted as a success in the below loop
- pingCounter++;
- successCounter++;
- }
-
- if (V) {
- Slog.v(TAG, (dnsAlive ? " +" : " Ignored: -"));
- }
-
- if (shouldCancel()) return false;
-
- try {
- Thread.sleep(pingDelay);
- } catch (InterruptedException e) {
- Slog.w(TAG, "Interrupted while pausing between pings", e);
- }
- }
-
- // Do the pings that we use to measure packet loss
- for (; pingCounter < numPings; pingCounter++) {
- if (shouldCancel()) return false;
-
- if (DnsPinger.isDnsReachable(dns, getPingTimeoutMs())) {
- successCounter++;
- if (V) {
- Slog.v(TAG, " +");
- }
- } else {
- if (V) {
- Slog.v(TAG, " -");
- }
- }
-
- if (shouldCancel()) return false;
-
- try {
- Thread.sleep(pingDelay);
- } catch (InterruptedException e) {
- Slog.w(TAG, "Interrupted while pausing between pings", e);
- }
- }
-
- //TODO: Integer division might cause problems down the road...
- int packetLossPercentage = 100 * (numPings - successCounter) / numPings;
- if (D) {
- Slog.d(TAG, packetLossPercentage
- + "% packet loss (acceptable is " + acceptableLoss + "%)");
- }
-
- return !shouldCancel() && (packetLossPercentage <= acceptableLoss);
- }
-
- private boolean backgroundCheckDnsConnectivity() {
- InetAddress dns = getDns();
-
- if (dns == null) {
- if (V) {
- myLogV("backgroundCheckDnsConnectivity: DNS is empty, returning false");
- }
- return false;
- }
-
- if (V) {
- myLogV("backgroundCheckDnsConnectivity: Background checking " +
- dns.getHostAddress() + " for connectivity");
- }
-
- return DnsPinger.isDnsReachable(dns, getBackgroundCheckTimeoutMs());
- }
-
- /**
- * Signals the current action to cancel.
- */
- private void cancelCurrentAction() {
- mShouldCancel = true;
- }
-
- /**
- * Helper to check whether to cancel.
- *
- * @return Whether to cancel processing the action.
- */
- private boolean shouldCancel() {
- if (V && mShouldCancel) {
- myLogV("shouldCancel: Cancelling");
- }
-
- return mShouldCancel;
- }
-
- // Wi-Fi initiated callbacks (could be executed in another thread)
-
- /**
- * Called when connected to an AP (this can be the next AP in line, or
- * it can be a completely different network).
- *
- * @param ssid The SSID of the access point.
- * @param bssid The BSSID of the access point.
- */
- private void onConnected(String ssid, String bssid) {
- if (V) {
- myLogV("onConnected: SSID: " + ssid + ", BSSID: " + bssid);
- }
-
- /*
- * The current action being processed by the main watchdog thread is now
- * stale, so cancel it.
- */
- cancelCurrentAction();
-
- if ((mSsid == null) || !mSsid.equals(ssid)) {
- /*
- * This is a different network than what the main watchdog thread is
- * processing, dispatch the network change message on the main thread.
- */
- mHandler.dispatchNetworkChanged(ssid);
- }
-
- if (requiresWatchdog(ssid, bssid)) {
- if (D) {
- myLogD(ssid + " (" + bssid + ") requires the watchdog");
- }
-
- // This access point requires a watchdog, so queue the check on the main thread
- mHandler.checkAp(new AccessPoint(ssid, bssid));
-
- } else {
- if (D) {
- myLogD(ssid + " (" + bssid + ") does not require the watchdog");
- }
-
- // This access point does not require a watchdog, so queue idle on the main thread
- mHandler.idle();
- }
- if (isWalledGardenTestEnabled()) mHandler.checkWalledGarden(ssid);
- }
-
- /**
- * Called when Wi-Fi is enabled.
- */
- private void onEnabled() {
- cancelCurrentAction();
- // Queue a hard-reset of the state on the main thread
- mHandler.reset();
- }
-
- /**
- * Called when disconnected (or some other event similar to being disconnected).
- */
- private void onDisconnected() {
- if (V) {
- myLogV("onDisconnected");
- }
-
- /*
- * Disconnected from an access point, the action being processed by the
- * watchdog thread is now stale, so cancel it.
- */
- cancelCurrentAction();
- // Dispatch the disconnected to the main watchdog thread
- mHandler.dispatchDisconnected();
- // Queue the action to go idle
- mHandler.idle();
- }
-
- /**
- * Checks whether an access point requires watchdog monitoring.
- *
- * @param ssid The SSID of the access point.
- * @param bssid The BSSID of the access point.
- * @return Whether the access point/network should be monitored by the
- * watchdog.
- */
- private boolean requiresWatchdog(String ssid, String bssid) {
- if (V) {
- myLogV("requiresWatchdog: SSID: " + ssid + ", BSSID: " + bssid);
- }
-
- WifiInfo info = null;
- if (ssid == null) {
- /*
- * This is called from a Wi-Fi callback, so assume the WifiInfo does
- * not have stale data.
- */
- info = mWifiManager.getConnectionInfo();
- ssid = info.getSSID();
- if (ssid == null) {
- // It's still null, give up
- if (V) {
- Slog.v(TAG, " Invalid SSID, returning false");
- }
- return false;
- }
- }
-
- if (TextUtils.isEmpty(bssid)) {
- // Similar as above
- if (info == null) {
- info = mWifiManager.getConnectionInfo();
- }
- bssid = info.getBSSID();
- if (TextUtils.isEmpty(bssid)) {
- // It's still null, give up
- if (V) {
- Slog.v(TAG, " Invalid BSSID, returning false");
- }
- return false;
- }
- }
-
- if (!isOnWatchList(ssid)) {
- if (V) {
- Slog.v(TAG, " SSID not on watch list, returning false");
- }
- return false;
- }
-
- // The watchdog only monitors networks with multiple APs
- if (!hasRequiredNumberOfAps(ssid)) {
- return false;
- }
-
- return true;
- }
-
- private boolean isOnWatchList(String ssid) {
- String watchList;
-
- if (ssid == null || (watchList = getWatchList()) == null) {
- return false;
- }
-
- String[] list = watchList.split(" *, *");
-
- for (String name : list) {
- if (ssid.equals(name)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Checks if the current scan results have multiple access points with an SSID.
- *
- * @param ssid The SSID to check.
- * @return Whether the SSID has multiple access points.
- */
- private boolean hasRequiredNumberOfAps(String ssid) {
- List<ScanResult> results = mWifiManager.getScanResults();
- if (results == null) {
- if (V) {
- myLogV("hasRequiredNumberOfAps: Got null scan results, returning false");
- }
- return false;
- }
-
- int numApsRequired = getApCount();
- int numApsFound = 0;
- int resultsSize = results.size();
- for (int i = 0; i < resultsSize; i++) {
- ScanResult result = results.get(i);
- if (result == null) continue;
- if (result.SSID == null) continue;
-
- if (result.SSID.equals(ssid)) {
- numApsFound++;
-
- if (numApsFound >= numApsRequired) {
- if (V) {
- myLogV("hasRequiredNumberOfAps: SSID: " + ssid + ", returning true");
- }
- return true;
- }
- }
- }
-
- if (V) {
- myLogV("hasRequiredNumberOfAps: SSID: " + ssid + ", returning false");
- }
- return false;
- }
-
- // Watchdog logic (assume all of these methods will be in our main thread)
-
- /**
- * Handles a Wi-Fi network change (for example, from networkA to networkB).
- */
- private void handleNetworkChanged(String ssid) {
- // Set the SSID being monitored to the new SSID
- mSsid = ssid;
- // Set various state to that when being idle
- setIdleState(true);
- }
-
- /**
- * Handles checking whether an AP is a "good" AP. If not, it will be blacklisted.
- *
- * @param ap The access point to check.
- */
- private void handleCheckAp(AccessPoint ap) {
- // Reset the cancel state since this is the entry point of this action
- mShouldCancel = false;
-
- if (V) {
- myLogV("handleCheckAp: AccessPoint: " + ap);
- }
-
- // Make sure we are not sleeping
- if (mState == WatchdogState.SLEEP) {
- if (V) {
- Slog.v(TAG, " Sleeping (in " + mSsid + "), so returning");
- }
- return;
- }
-
- mState = WatchdogState.CHECKING_AP;
-
- /*
- * Checks to make sure we haven't exceeded the max number of checks
- * we're allowed per network
- */
- incrementBssidCheckCount();
- if (getBssidCheckCount() > getMaxApChecks()) {
- if (V) {
- Slog.v(TAG, " Passed the max attempts (" + getMaxApChecks()
- + "), going to sleep for " + mSsid);
- }
- mHandler.sleep(mSsid);
- return;
- }
-
- // Do the check
- boolean isApAlive = checkDnsConnectivity();
-
- if (V) {
- Slog.v(TAG, " Is it alive: " + isApAlive);
- }
-
- // Take action based on results
- if (isApAlive) {
- handleApAlive(ap);
- } else {
- handleApUnresponsive(ap);
- }
- }
-
- /**
- * Handles the case when an access point is alive.
- *
- * @param ap The access point.
- */
- private void handleApAlive(AccessPoint ap) {
- // Check whether we are stale and should cancel
- if (shouldCancel()) return;
- // We're satisfied with this AP, so go idle
- setIdleState(false);
-
- if (D) {
- myLogD("AP is alive: " + ap.toString());
- }
-
- // Queue the next action to be a background check
- mHandler.backgroundCheckAp(ap);
- }
-
- /**
- * Handles an unresponsive AP by blacklisting it.
- *
- * @param ap The access point.
- */
- private void handleApUnresponsive(AccessPoint ap) {
- // Check whether we are stale and should cancel
- if (shouldCancel()) return;
- // This AP is "bad", switch to another
- mState = WatchdogState.SWITCHING_AP;
-
- if (D) {
- myLogD("AP is dead: " + ap.toString());
- }
-
- // Black list this "bad" AP, this will cause an attempt to connect to another
- blacklistAp(ap.bssid);
- // Initiate an association to an alternate AP
- mWifiManager.reassociate();
- }
-
- private void blacklistAp(String bssid) {
- if (TextUtils.isEmpty(bssid)) {
- return;
- }
-
- // Before taking action, make sure we should not cancel our processing
- if (shouldCancel()) return;
-
- mWifiManager.addToBlacklist(bssid);
-
- if (D) {
- myLogD("Blacklisting " + bssid);
- }
- }
-
- /**
- * Handles a single background check. If it fails, it should trigger a
- * normal check. If it succeeds, it should queue another background check.
- *
- * @param ap The access point to do a background check for. If this is no
- * longer the current AP, it is okay to return without any
- * processing.
- */
- private void handleBackgroundCheckAp(AccessPoint ap) {
- // Reset the cancel state since this is the entry point of this action
- mShouldCancel = false;
-
- if (V) {
- myLogV("handleBackgroundCheckAp: AccessPoint: " + ap);
- }
-
- // Make sure we are not sleeping
- if (mState == WatchdogState.SLEEP) {
- if (V) {
- Slog.v(TAG, " handleBackgroundCheckAp: Sleeping (in " + mSsid + "), so returning");
- }
- return;
- }
-
- // Make sure the AP we're supposed to be background checking is still the active one
- WifiInfo info = mWifiManager.getConnectionInfo();
- if (info.getSSID() == null || !info.getSSID().equals(ap.ssid)) {
- if (V) {
- myLogV("handleBackgroundCheckAp: We are no longer connected to "
- + ap + ", and instead are on " + info);
- }
- return;
- }
-
- if (info.getBSSID() == null || !info.getBSSID().equals(ap.bssid)) {
- if (V) {
- myLogV("handleBackgroundCheckAp: We are no longer connected to "
- + ap + ", and instead are on " + info);
- }
- return;
- }
-
- // Do the check
- boolean isApAlive = backgroundCheckDnsConnectivity();
-
- if (V && !isApAlive) {
- Slog.v(TAG, " handleBackgroundCheckAp: Is it alive: " + isApAlive);
- }
-
- if (shouldCancel()) {
- return;
- }
-
- // Take action based on results
- if (isApAlive) {
- // Queue another background check
- mHandler.backgroundCheckAp(ap);
-
- } else {
- if (D) {
- myLogD("Background check failed for " + ap.toString());
- }
-
- // Queue a normal check, so it can take proper action
- mHandler.checkAp(ap);
- }
- }
-
- /**
- * Handles going to sleep for this network. Going to sleep means we will not
- * monitor this network anymore.
- *
- * @param ssid The network that will not be monitored anymore.
- */
- private void handleSleep(String ssid) {
- // Make sure the network we're trying to sleep in is still the current network
- if (ssid != null && ssid.equals(mSsid)) {
- mState = WatchdogState.SLEEP;
-
- if (D) {
- myLogD("Going to sleep for " + ssid);
- }
-
- /*
- * Before deciding to go to sleep, we may have checked a few APs
- * (and blacklisted them). Clear the blacklist so the AP with best
- * signal is chosen.
- */
- mWifiManager.clearBlacklist();
-
- if (V) {
- myLogV("handleSleep: Set state to SLEEP and cleared blacklist");
- }
- }
- }
-
- /**
- * Handles an access point disconnection.
- */
- private void handleDisconnected() {
- /*
- * We purposefully do not change mSsid to null. This is to handle
- * disconnected followed by connected better (even if there is some
- * duration in between). For example, if the watchdog went to sleep in a
- * network, and then the phone goes to sleep, when the phone wakes up we
- * still want to be in the sleeping state. When the phone went to sleep,
- * we would have gotten a disconnected event which would then set mSsid
- * = null. This is bad, since the following connect would cause us to do
- * the "network is good?" check all over again. */
-
- /*
- * Set the state as if we were idle (don't come out of sleep, only
- * hard reset and network changed should do that.
- */
- setIdleState(false);
- }
-
- /**
- * Handles going idle. Idle means we are satisfied with the current state of
- * things, but if a new connection occurs we'll re-evaluate.
- */
- private void handleIdle() {
- // Reset the cancel state since this is the entry point for this action
- mShouldCancel = false;
-
- if (V) {
- myLogV("handleSwitchToIdle");
- }
-
- // If we're sleeping, don't do anything
- if (mState == WatchdogState.SLEEP) {
- Slog.v(TAG, " Sleeping (in " + mSsid + "), so returning");
- return;
- }
-
- // Set the idle state
- setIdleState(false);
-
- if (V) {
- Slog.v(TAG, " Set state to IDLE");
- }
- }
-
- /**
- * Sets the state as if we are going idle.
- */
- private void setIdleState(boolean forceIdleState) {
- // Setting idle state does not kick us out of sleep unless the forceIdleState is set
- if (forceIdleState || (mState != WatchdogState.SLEEP)) {
- mState = WatchdogState.IDLE;
- }
- resetBssidCheckCount();
- }
-
- /**
- * Handles a hard reset. A hard reset is rarely used, but when used it
- * should revert anything done by the watchdog monitoring.
- */
- private void handleReset() {
- mWifiManager.clearBlacklist();
- setIdleState(true);
- }
-
- // Inner classes
-
- /**
- * Possible states for the watchdog to be in.
- */
- private static enum WatchdogState {
- /** The watchdog is currently idle, but it is still responsive to future AP checks in this network. */
- IDLE,
- /** The watchdog is sleeping, so it will not try any AP checks for the network. */
- SLEEP,
- /** The watchdog is currently checking an AP for connectivity. */
- CHECKING_AP,
- /** The watchdog is switching to another AP in the network. */
- SWITCHING_AP
- }
-
- private int getBssidCheckCount() {
- return mBssidCheckCount;
- }
-
- private void incrementBssidCheckCount() {
- mBssidCheckCount++;
- }
-
- private void resetBssidCheckCount() {
- this.mBssidCheckCount = 0;
- }
-
- /**
- * The main thread for the watchdog monitoring. This will be turned into a
- * {@link Looper} thread.
- */
- private class WifiWatchdogThread extends Thread {
- WifiWatchdogThread() {
- super("WifiWatchdogThread");
- }
-
- @Override
- public void run() {
- // Set this thread up so the handler will work on it
- Looper.prepare();
-
- synchronized(WifiWatchdogService.this) {
- mHandler = new WifiWatchdogHandler();
-
- // Notify that the handler has been created
- WifiWatchdogService.this.notify();
- }
-
- // Listen for messages to the handler
- Looper.loop();
- }
- }
-
- /**
- * The main thread's handler. There are 'actions', and just general
- * 'messages'. There should only ever be one 'action' in the queue (aside
- * from the one being processed, if any). There may be multiple messages in
- * the queue. So, actions are replaced by more recent actions, where as
- * messages will be executed for sure. Messages end up being used to just
- * change some state, and not really take any action.
- * <p>
- * There is little logic inside this class, instead methods of the form
- * "handle___" are called in the main {@link WifiWatchdogService}.
- */
- private class WifiWatchdogHandler extends Handler {
- /** Check whether the AP is "good". The object will be an {@link AccessPoint}. */
- static final int ACTION_CHECK_AP = 1;
- /** Go into the idle state. */
- static final int ACTION_IDLE = 2;
- /**
- * Performs a periodic background check whether the AP is still "good".
- * 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
- * this a message rather than action. We want to make sure our main
- * thread sees this message, but if it were an action it could be
- * removed from the queue and replaced by another action. The main
- * thread will ensure when it sees the message that the state is still
- * valid for going to sleep.
- * <p>
- * For an explanation of sleep, see {@link android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS}.
- */
- static final int MESSAGE_SLEEP = 101;
- /** Disables the watchdog. */
- static final int MESSAGE_DISABLE_WATCHDOG = 102;
- /** The network has changed. */
- static final int MESSAGE_NETWORK_CHANGED = 103;
- /** The current access point has disconnected. */
- 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));
- }
-
- void backgroundCheckAp(AccessPoint ap) {
- if (!isBackgroundCheckEnabled()) return;
-
- removeAllActions();
- sendMessageDelayed(obtainMessage(ACTION_BACKGROUND_CHECK_AP, ap),
- getBackgroundCheckDelayMs());
- }
-
- void idle() {
- removeAllActions();
- sendMessage(obtainMessage(ACTION_IDLE));
- }
-
- void sleep(String ssid) {
- removeAllActions();
- sendMessage(obtainMessage(MESSAGE_SLEEP, ssid));
- }
-
- void disableWatchdog() {
- removeAllActions();
- sendMessage(obtainMessage(MESSAGE_DISABLE_WATCHDOG));
- }
-
- void dispatchNetworkChanged(String ssid) {
- removeAllActions();
- sendMessage(obtainMessage(MESSAGE_NETWORK_CHANGED, ssid));
- }
-
- void dispatchDisconnected() {
- removeAllActions();
- sendMessage(obtainMessage(MESSAGE_DISCONNECTED));
- }
-
- void reset() {
- removeAllActions();
- sendMessage(obtainMessage(MESSAGE_RESET));
- }
-
- private void removeAllActions() {
- removeMessages(ACTION_CHECK_AP);
- removeMessages(ACTION_IDLE);
- removeMessages(ACTION_BACKGROUND_CHECK_AP);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (V) {
- myLogV("handleMessage: " + msg.what);
- }
- switch (msg.what) {
- case MESSAGE_NETWORK_CHANGED:
- handleNetworkChanged((String) msg.obj);
- break;
- case ACTION_CHECK_AP:
- handleCheckAp((AccessPoint) msg.obj);
- break;
- 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;
- case ACTION_IDLE:
- handleIdle();
- break;
- case MESSAGE_DISABLE_WATCHDOG:
- handleIdle();
- break;
- case MESSAGE_DISCONNECTED:
- handleDisconnected();
- break;
- case MESSAGE_RESET:
- handleReset();
- break;
- }
- }
-
- /**
- * DNS based detection techniques do not work at all hotspots. The one sure way to check
- * a walled garden is to see if a URL fetch on a known address fetches the data we
- * expect
- */
- private boolean isWalledGardenConnection() {
- InputStream in = null;
- HttpURLConnection urlConnection = null;
- try {
- URL url = new URL(getWalledGardenUrl());
- urlConnection = (HttpURLConnection) url.openConnection();
- in = new BufferedInputStream(urlConnection.getInputStream());
- Scanner scanner = new Scanner(in);
- if (scanner.findInLine(getWalledGardenPattern()) != null) {
- return false;
- } else {
- return true;
- }
- } catch (IOException e) {
- return false;
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- }
- }
- if (urlConnection != null) urlConnection.disconnect();
- }
- }
-
- 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);
- }
- }
- }
-
- /**
- * Receives Wi-Fi broadcasts.
- * <p>
- * There is little logic in this class, instead methods of the form "on___"
- * are called in the {@link WifiWatchdogService}.
- */
- private BroadcastReceiver mReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- handleNetworkStateChanged(
- (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO));
- } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- handleWifiStateChanged(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN));
- }
- }
-
- private void handleNetworkStateChanged(NetworkInfo info) {
- if (V) {
- myLogV("Receiver.handleNetworkStateChanged: NetworkInfo: "
- + info);
- }
-
- switch (info.getState()) {
- case CONNECTED:
- WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
- if (V) {
- myLogV("handleNetworkStateChanged: Got connected event but SSID or BSSID are null. SSID: "
- + wifiInfo.getSSID()
- + ", BSSID: "
- + wifiInfo.getBSSID() + ", ignoring event");
- }
- return;
- }
- onConnected(wifiInfo.getSSID(), wifiInfo.getBSSID());
- break;
-
- case DISCONNECTED:
- onDisconnected();
- break;
- }
- }
-
- private void handleWifiStateChanged(int wifiState) {
- if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
- onDisconnected();
- } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
- onEnabled();
- }
- }
- };
-
- /**
- * Describes an access point by its SSID and BSSID.
- *
- */
- private static class AccessPoint {
- String ssid;
- String bssid;
-
- /**
- * @param ssid cannot be null
- * @param bssid cannot be null
- */
- AccessPoint(String ssid, String bssid) {
- if (ssid == null || bssid == null) {
- Slog.e(TAG, String.format("(%s) INVALID ACCESSPOINT: (%s, %s)",
- Thread.currentThread().getName(),ssid,bssid));
- }
- this.ssid = ssid;
- this.bssid = bssid;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof AccessPoint)) return false;
- AccessPoint otherAp = (AccessPoint) o;
-
- // Either we both have a null, or our SSIDs and BSSIDs are equal
- return ssid.equals(otherAp.ssid) && bssid.equals(otherAp.bssid);
- }
-
- @Override
- public int hashCode() {
- return ssid.hashCode() + bssid.hashCode();
- }
-
- @Override
- public String toString() {
- return ssid + " (" + bssid + ")";
- }
- }
-
- /**
- * Performs a simple DNS "ping" by sending a "server status" query packet to
- * the DNS server. As long as the server replies, we consider it a success.
- * <p>
- * We do not use a simple hostname lookup because that could be cached and
- * the API may not differentiate between a time out and a failure lookup
- * (which we really care about).
- */
- private static class DnsPinger {
-
- /** Number of bytes for the query */
- private static final int DNS_QUERY_BASE_SIZE = 33;
-
- /** The DNS port */
- private static final int DNS_PORT = 53;
-
- /** Used to generate IDs */
- private static Random sRandom = new Random();
-
- static boolean isDnsReachable(InetAddress dnsAddress, int timeout) {
- DatagramSocket socket = null;
- try {
- socket = new DatagramSocket();
-
- // Set some socket properties
- socket.setSoTimeout(timeout);
-
- byte[] buf = new byte[DNS_QUERY_BASE_SIZE];
- fillQuery(buf);
-
- // Send the DNS query
-
- DatagramPacket packet = new DatagramPacket(buf,
- buf.length, dnsAddress, DNS_PORT);
- socket.send(packet);
-
- // Wait for reply (blocks for the above timeout)
- DatagramPacket replyPacket = new DatagramPacket(buf, buf.length);
- socket.receive(replyPacket);
-
- // If a timeout occurred, an exception would have been thrown. We got a reply!
- return true;
-
- } catch (SocketException e) {
- if (V) {
- Slog.v(TAG, "DnsPinger.isReachable received SocketException", e);
- }
- return false;
-
- } catch (UnknownHostException e) {
- if (V) {
- Slog.v(TAG, "DnsPinger.isReachable is unable to resolve the DNS host", e);
- }
- return false;
-
- } catch (SocketTimeoutException e) {
- return false;
-
- } catch (IOException e) {
- if (V) {
- Slog.v(TAG, "DnsPinger.isReachable got an IOException", e);
- }
- return false;
-
- } catch (Exception e) {
- if (V) {
- Slog.d(TAG, "DnsPinger.isReachable got an unknown exception", e);
- }
- return false;
- } finally {
- if (socket != null) {
- socket.close();
- }
- }
- }
-
- private static void fillQuery(byte[] buf) {
-
- /*
- * See RFC2929 (though the bit tables in there are misleading for
- * us. For example, the recursion desired bit is the 0th bit for us,
- * but looking there it would appear as the 7th bit of the byte
- */
-
- // Make sure it's all zeroed out
- for (int i = 0; i < buf.length; i++) buf[i] = 0;
-
- // Form a query for www.android.com
-
- // [0-1] bytes are an ID, generate random ID for this query
- buf[0] = (byte) sRandom.nextInt(256);
- buf[1] = (byte) sRandom.nextInt(256);
-
- // [2-3] bytes are for flags.
- buf[2] = 1; // Recursion desired
-
- // [4-5] bytes are for the query count
- buf[5] = 1; // One query
-
- // [6-7] [8-9] [10-11] are all counts of other fields we don't use
-
- // [12-15] for www
- writeString(buf, 12, "www");
-
- // [16-23] for android
- writeString(buf, 16, "android");
-
- // [24-27] for com
- writeString(buf, 24, "com");
-
- // [29-30] bytes are for QTYPE, set to 1
- buf[30] = 1;
-
- // [31-32] bytes are for QCLASS, set to 1
- buf[32] = 1;
- }
-
- private static void writeString(byte[] buf, int startPos, String string) {
- int pos = startPos;
-
- // Write the length first
- buf[pos++] = (byte) string.length();
- for (int i = 0; i < string.length(); i++) {
- buf[pos++] = (byte) string.charAt(i);
- }
- }
- }
-}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index f6dd43a..80cdf6b 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -436,7 +436,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public IAccessibilityServiceConnection registerEventListener(IEventListener listener) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
FUNCTION_REGISTER_EVENT_LISTENER);
- ComponentName componentName = new ComponentName("foo.bar", "FakeAccessibilityService");
+ ComponentName componentName = new ComponentName("foo.bar",
+ "AutomationAccessibilityService");
synchronized (mLock) {
Service oldService = mComponentNameToServiceMap.get(componentName);
if (oldService != null) {
@@ -550,6 +551,26 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void notifyEventListenerLocked(Service service, int eventType) {
IEventListener listener = service.mServiceInterface;
AccessibilityEvent event = service.mPendingEvents.get(eventType);
+
+ // Check for null here because there is a concurrent scenario in which this
+ // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
+ // which posts a message for dispatching an event. 2) The message is pulled
+ // from the queue by the handler on the service thread and the latter is
+ // just about to acquire the lock and call this method. 3) Now another binder
+ // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked
+ // so the service thread waits for the lock; 4) The binder thread replaces
+ // the event with a more recent one (assume the same event type) and posts a
+ // dispatch request releasing the lock. 5) Now the main thread is unblocked and
+ // dispatches the event which is removed from the pending ones. 6) And ... now
+ // the service thread handles the last message posted by the last binder call
+ // but the event is already dispatched and hence looking it up in the pending
+ // ones yields null. This check is much simpler that keeping count for each
+ // event type of each service to catch such a scenario since only one message
+ // is processed at a time.
+ if (event == null) {
+ return;
+ }
+
service.mPendingEvents.remove(eventType);
try {
if (mSecurityPolicy.canRetrieveWindowContent(service)) {
@@ -809,7 +830,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
boolean mCanRetrieveScreenContent;
- boolean mIsFake;
+ boolean mIsAutomation;
final Callback mCallback = new Callback();
@@ -822,12 +843,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
new SparseArray<AccessibilityEvent>();
public Service(ComponentName componentName,
- AccessibilityServiceInfo accessibilityServiceInfo, boolean isFake) {
+ AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) {
mId = sIdCounter++;
mComponentName = componentName;
mAccessibilityServiceInfo = accessibilityServiceInfo;
- mIsFake = isFake;
- if (!isFake) {
+ mIsAutomation = isAutomation;
+ if (!isAutomation) {
mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent();
mIntent = new Intent().setComponent(mComponentName);
mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
@@ -861,7 +882,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @return True if binding is successful.
*/
public boolean bind() {
- if (!mIsFake && mService == null) {
+ if (!mIsAutomation && mService == null) {
return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
}
return false;
@@ -878,7 +899,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
tryRemoveServiceLocked(this);
}
- if (!mIsFake) {
+ if (!mIsAutomation) {
mContext.unbindService(this);
}
mService = null;
@@ -918,16 +939,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
- if (permissionGranted) {
+ if (!permissionGranted) {
+ return null;
+ } else {
connection = getConnectionToRetrievalAllowingWindowLocked();
+ if (connection == null) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to a retrieve "
+ + "allowing window.");
+ }
+ return null;
+ }
}
}
- if (connection == null) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "No interaction connection to a retrieve allowing window.");
- }
- return null;
- }
final long identityToken = Binder.clearCallingIdentity();
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
@@ -962,16 +986,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
- if (permissionGranted) {
+ if (!permissionGranted) {
+ return null;
+ } else {
connection = getConnectionToRetrievalAllowingWindowLocked();
+ if (connection == null) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to focused window.");
+ }
+ return null;
+ }
}
}
- if (connection == null) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "No interaction connection to focused window.");
- }
- return null;
- }
final long identityToken = Binder.clearCallingIdentity();
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
@@ -1005,17 +1031,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
- if (permissionGranted) {
+ if (!permissionGranted) {
+ return null;
+ } else {
connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
+ if (connection == null) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to window: "
+ + accessibilityWindowId);
+ }
+ return null;
+ }
}
}
- if (connection == null) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "No interaction connection to window: "
- + accessibilityWindowId);
- }
- return null;
- }
final long identityToken = Binder.clearCallingIdentity();
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
@@ -1046,17 +1074,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
accessibilityWindowId, action);
- if (permissionGranted) {
+ if (!permissionGranted) {
+ return false;
+ } else {
connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
+ if (connection == null) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to window: "
+ + accessibilityWindowId);
+ }
+ return false;
+ }
}
}
- if (connection == null) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "No interaction connection to window: "
- + accessibilityWindowId);
- }
- return false;
- }
final long identityToken = Binder.clearCallingIdentity();
try {
final int interactionId = mInteractionIdCounter.getAndIncrement();
@@ -1131,7 +1161,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
| AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
| AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
| AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED
- | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+ | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
+ | AccessibilityEvent.TYPE_VIEW_SCROLLED;
private int mRetrievalAlowingWindowId;
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index aab189a..1af7015 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -26,7 +26,6 @@ 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;
@@ -73,15 +72,6 @@ public class TouchExplorer implements Explorer {
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;
@@ -189,7 +179,7 @@ public class TouchExplorer implements Explorer {
if (DEBUG) {
Slog.d(LOG_TAG_RECEIVED, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
- Slog.d(LOG_TAG_STATE, sStateSymbolicNames.get(mCurrentState));
+ Slog.d(LOG_TAG_STATE, getStateSymbolicName(mCurrentState));
}
// Keep track of the pointers's state.
@@ -708,8 +698,7 @@ public class TouchExplorer implements Explorer {
private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) {
final PointerProperties[] pointerProperties = mTempPointerProperties;
final PointerCoords[] pointerCoords = mTempPointerCoords;
- final int pointerId = mPointerTracker.getLastReceivedUpPointerId();
- final int pointerIndex = prototype.findPointerIndex(pointerId);
+ final int pointerIndex = prototype.getActionIndex();
// Send down.
prototype.getPointerProperties(pointerIndex, pointerProperties[0]);
@@ -884,6 +873,25 @@ public class TouchExplorer implements Explorer {
}
/**
+ * Gets the symbolic name of a state.
+ *
+ * @param state A state.
+ * @return The state symbolic name.
+ */
+ private static String getStateSymbolicName(int state) {
+ switch (state) {
+ case STATE_TOUCH_EXPLORING:
+ return "STATE_TOUCH_EXPLORING";
+ case STATE_DRAGGING:
+ return "STATE_DRAGGING";
+ case STATE_DELEGATING:
+ return "STATE_DELEGATING";
+ default:
+ throw new IllegalArgumentException("Unknown state: " + state);
+ }
+ }
+
+ /**
* 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
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4ec71c1..d5e8730 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -502,15 +502,6 @@ public final class ActivityManagerService extends ActivityManagerNative
= new ArrayList<ProcessRecord>();
/**
- * List of records for processes that we have started and are waiting
- * for them to call back. This is really only needed when running in
- * single processes mode, in which case we do not have a unique pid for
- * each process.
- */
- final ArrayList<ProcessRecord> mStartingProcesses
- = new ArrayList<ProcessRecord>();
-
- /**
* List of persistent applications that are in the process
* of being started.
*/
@@ -622,6 +613,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
return true;
}
+
+ @Override
+ protected String packageForFilter(BroadcastFilter filter) {
+ return filter.packageName;
+ }
};
/**
@@ -1825,6 +1821,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// We already have the app running, or are waiting for it to
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
+ // If this is a new package in the process, add the package to the list
+ app.addPackage(info.packageName);
return app;
} else {
// An application record is attached to a previous process,
@@ -1946,11 +1944,15 @@ public final class ActivityManagerService extends ActivityManagerNative
if ("1".equals(SystemProperties.get("debug.checkjni"))) {
debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
+ if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
+ debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
+ }
if ("1".equals(SystemProperties.get("debug.assert"))) {
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
int pid = Process.start("android.app.ActivityThread",
- app.processName, uid, uid, gids, debugFlags, null);
+ app.processName, uid, uid, gids, debugFlags,
+ app.info.targetSdkVersion, null);
BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
synchronized (bs) {
if (bs.isOnBattery()) {
@@ -1990,12 +1992,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
buf.append("}");
Slog.i(TAG, buf.toString());
- if (pid == 0 || pid == MY_PID) {
- // Processes are being emulated with threads.
- app.pid = MY_PID;
- app.removed = false;
- mStartingProcesses.add(app);
- } else if (pid > 0) {
+ if (pid > 0) {
app.pid = pid;
app.removed = false;
synchronized (mPidsSelfLocked) {
@@ -2278,7 +2275,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- return pir.sendInner(0, fillInIntent, resolvedType,
+ return pir.sendInner(0, fillInIntent, resolvedType, null,
null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
}
@@ -3595,9 +3592,6 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
- } else if (mStartingProcesses.size() > 0) {
- app = mStartingProcesses.remove(0);
- app.setPid(pid);
} else {
app = null;
}
@@ -4031,8 +4025,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
int callingUid = Binder.getCallingUid();
try {
- if (callingUid != 0 && callingUid != Process.SYSTEM_UID &&
- Process.supportsProcesses()) {
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
.getPackageUid(packageName);
if (uid != Binder.getCallingUid()) {
@@ -4162,6 +4155,27 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
+ public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) {
+ if (!(pendingResult instanceof PendingIntentRecord)) {
+ return false;
+ }
+ try {
+ PendingIntentRecord res = (PendingIntentRecord)pendingResult;
+ if (res.key.allIntents == null) {
+ return false;
+ }
+ for (int i=0; i<res.key.allIntents.length; i++) {
+ Intent intent = res.key.allIntents[i];
+ if (intent.getPackage() != null && intent.getComponent() != null) {
+ return false;
+ }
+ }
+ return true;
+ } catch (ClassCastException e) {
+ }
+ return false;
+ }
+
public void setProcessLimit(int max) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessLimit()");
@@ -4270,8 +4284,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// Root, system server and our own process get to do everything.
- if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID ||
- !Process.supportsProcesses()) {
+ if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
// If there is a uid that owns whatever is being accessed, it has
@@ -4415,7 +4428,7 @@ public final class ActivityManagerService extends ActivityManagerNative
private final boolean checkUriPermissionLocked(Uri uri, int uid,
int modeFlags) {
// Root gets to do everything.
- if (uid == 0 || !Process.supportsProcesses()) {
+ if (uid == 0) {
return true;
}
HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(uid);
@@ -5143,7 +5156,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- private void removeTaskProcessesLocked(ActivityRecord root) {
+ private void cleanUpRemovedTaskLocked(ActivityRecord root, boolean killProcesses) {
TaskRecord tr = root.task;
Intent baseIntent = new Intent(
tr.intent != null ? tr.intent : tr.affinityIntent);
@@ -5166,6 +5179,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ServiceRecord sr = services.get(i);
if (sr.startRequested) {
if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
+ Slog.i(TAG, "Stopping service " + sr.shortName + ": remove task");
stopServiceLocked(sr);
} else {
sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
@@ -5177,26 +5191,28 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- // 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";
+ if (killProcesses) {
+ // 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.toShortString() + ": remove task");
+ EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid,
+ pr.processName, pr.setAdj, "remove task");
+ Process.killProcessQuiet(pr.pid);
+ } else {
+ pr.waitingToKill = "remove task";
+ }
}
}
}
@@ -5210,11 +5226,8 @@ public final class ActivityManagerService extends ActivityManagerNative
ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1);
if (r != null) {
mRecentTasks.remove(r.task);
-
- if ((flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0) {
- removeTaskProcessesLocked(r);
- }
-
+ cleanUpRemovedTaskLocked(r,
+ (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0);
return true;
}
} finally {
@@ -5496,8 +5509,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// CONTENT PROVIDERS
// =========================================================
- private final List generateApplicationProvidersLocked(ProcessRecord app) {
- List providers = null;
+ private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
+ List<ProviderInfo> providers = null;
try {
providers = AppGlobals.getPackageManager().
queryContentProviders(app.processName, app.info.uid,
@@ -5935,7 +5948,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
public static final void installSystemProviders() {
- List providers;
+ List<ProviderInfo> providers;
synchronized (mSelf) {
ProcessRecord app = mSelf.mProcessNames.get("system", Process.SYSTEM_UID);
providers = mSelf.generateApplicationProvidersLocked(app);
@@ -6553,13 +6566,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
public void systemReady(final Runnable goingCallback) {
- // In the simulator, startRunning will never have been called, which
- // normally sets a few crucial variables. Do it here instead.
- if (!Process.supportsProcesses()) {
- mStartRunning = true;
- mTopAction = Intent.ACTION_MAIN;
- }
-
synchronized(this) {
if (mSystemReady) {
if (goingCallback != null) goingCallback.run();
@@ -7923,14 +7929,6 @@ public final class ActivityManagerService extends ActivityManagerNative
"Starting Norm", "Restarting PERS");
}
- if (mStartingProcesses.size() > 0) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Processes that are starting:");
- dumpProcessList(pw, this, mStartingProcesses, " ",
- "Starting Norm", "Starting PERS");
- }
-
if (mRemovedProcesses.size() > 0) {
if (needSep) pw.println(" ");
needSep = true;
@@ -9895,6 +9893,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
if (app != null && app.thread != null) {
try {
+ app.addPackage(r.appInfo.packageName);
realStartServiceLocked(r, app);
return true;
} catch (RemoteException e) {
@@ -10945,7 +10944,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mBroadcastsScheduled = true;
}
- public Intent registerReceiver(IApplicationThread caller,
+ public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission) {
synchronized(this) {
ProcessRecord callerApp = null;
@@ -10957,6 +10956,13 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
+ if (callerApp.info.uid != Process.SYSTEM_UID &&
+ !callerApp.pkgList.contains(callerPackage)) {
+ throw new SecurityException("Given caller package " + callerPackage
+ + " is not running in process " + callerApp);
+ }
+ } else {
+ callerPackage = null;
}
List allSticky = null;
@@ -11001,7 +11007,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
- BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
+ BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
@@ -12155,6 +12161,7 @@ public final class ActivityManagerService extends ActivityManagerNative
info.activityInfo.applicationInfo.uid);
if (app != null && app.thread != null) {
try {
+ app.addPackage(info.activityInfo.packageName);
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
@@ -13087,74 +13094,72 @@ public final class ActivityManagerService extends ActivityManagerNative
int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
- if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) {
- if (app.curRawAdj != app.setRawAdj) {
- if (app.curRawAdj > FOREGROUND_APP_ADJ
- && app.setRawAdj <= FOREGROUND_APP_ADJ) {
- // If this app is transitioning from foreground to
- // non-foreground, have it do a gc.
- scheduleAppGcLocked(app);
- } else if (app.curRawAdj >= HIDDEN_APP_MIN_ADJ
- && app.setRawAdj < HIDDEN_APP_MIN_ADJ) {
- // Likewise do a gc when an app is moving in to the
- // background (such as a service stopping).
- scheduleAppGcLocked(app);
- }
-
- if (wasKeeping && !app.keeping) {
- // This app is no longer something we want to keep. Note
- // its current wake lock time to later know to kill it if
- // it is not behaving well.
- BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
- synchronized (stats) {
- app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
- app.pid, SystemClock.elapsedRealtime());
- }
- app.lastCpuTime = app.curCpuTime;
+ if (app.curRawAdj != app.setRawAdj) {
+ if (app.curRawAdj > FOREGROUND_APP_ADJ
+ && app.setRawAdj <= FOREGROUND_APP_ADJ) {
+ // If this app is transitioning from foreground to
+ // non-foreground, have it do a gc.
+ scheduleAppGcLocked(app);
+ } else if (app.curRawAdj >= HIDDEN_APP_MIN_ADJ
+ && app.setRawAdj < HIDDEN_APP_MIN_ADJ) {
+ // Likewise do a gc when an app is moving in to the
+ // background (such as a service stopping).
+ scheduleAppGcLocked(app);
+ }
+
+ if (wasKeeping && !app.keeping) {
+ // This app is no longer something we want to keep. Note
+ // its current wake lock time to later know to kill it if
+ // it is not behaving well.
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, SystemClock.elapsedRealtime());
}
+ app.lastCpuTime = app.curCpuTime;
+ }
- app.setRawAdj = app.curRawAdj;
+ app.setRawAdj = app.curRawAdj;
+ }
+ if (adj != app.setAdj) {
+ if (Process.setOomAdj(app.pid, adj)) {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
+ TAG, "Set app " + app.processName +
+ " oom adj to " + adj);
+ app.setAdj = adj;
+ } else {
+ success = false;
}
- if (adj != app.setAdj) {
- if (Process.setOomAdj(app.pid, adj)) {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
- TAG, "Set app " + app.processName +
- " oom adj to " + adj);
- app.setAdj = adj;
- } else {
- success = false;
+ }
+ if (app.setSchedGroup != app.curSchedGroup) {
+ app.setSchedGroup = app.curSchedGroup;
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+ "Setting process group of " + app.processName
+ + " to " + app.curSchedGroup);
+ if (app.waitingToKill != null &&
+ app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ Slog.i(TAG, "Killing " + app.toShortString() + ": " + 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 {
+ 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 (app.setSchedGroup != app.curSchedGroup) {
- app.setSchedGroup = app.curSchedGroup;
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
- "Setting process group of " + app.processName
- + " to " + app.curSchedGroup);
- 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();
+ if (false) {
+ if (app.thread != null) {
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) {
- try {
- app.thread.setSchedulingGroup(app.curSchedGroup);
- } catch (RemoteException e) {
- }
+ app.thread.setSchedulingGroup(app.curSchedGroup);
+ } catch (RemoteException e) {
}
}
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index b94ee58..b1da69f 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -652,6 +652,7 @@ final class ActivityStack {
if (app != null && app.thread != null) {
try {
+ app.addPackage(r.info.packageName);
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index b4fdc9f..293702d 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -446,6 +446,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
+ private void dumpHelp(PrintWriter pw) {
+ pw.println("Battery stats (batteryinfo) dump options:");
+ pw.println(" [--checkin] [--reset] [--write] [-h]");
+ pw.println(" --checkin: format output for a checkin report.");
+ pw.println(" --reset: reset the stats, clearing all current data.");
+ pw.println(" --write: force write current collected stats to disk.");
+ pw.println(" -h: print this help text.");
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
boolean isCheckin = false;
@@ -466,8 +475,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
pw.println("Battery stats written.");
noOutput = true;
}
+ } else if ("-h".equals(arg)) {
+ dumpHelp(pw);
+ return;
} else {
pw.println("Unknown option: " + arg);
+ dumpHelp(pw);
}
}
}
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index 2e784d3..b49bc22 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -25,12 +25,14 @@ import java.io.PrintWriter;
class BroadcastFilter extends IntentFilter {
// Back-pointer to the list this filter is in.
final ReceiverList receiverList;
+ final String packageName;
final String requiredPermission;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
- String _requiredPermission) {
+ String _packageName, String _requiredPermission) {
super(_filter);
receiverList = _receiverList;
+ packageName = _packageName;
requiredPermission = _requiredPermission;
}
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index ee6e420..8ed0cc1 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -177,13 +177,13 @@ class PendingIntentRecord extends IIntentSender.Stub {
}
public int send(int code, Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver) {
+ IIntentReceiver finishedReceiver, String requiredPermission) {
return sendInner(code, intent, resolvedType, finishedReceiver,
- null, null, 0, 0, 0);
+ requiredPermission, null, null, 0, 0, 0);
}
int sendInner(int code, Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver,
+ IIntentReceiver finishedReceiver, String requiredPermission,
IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues) {
synchronized(owner) {
@@ -246,8 +246,8 @@ class PendingIntentRecord extends IIntentSender.Stub {
// that the broadcast be delivered synchronously
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
- finishedReceiver, code, null, null, null,
- (finishedReceiver != null), false);
+ finishedReceiver, code, null, null,
+ requiredPermission, (finishedReceiver != null), false);
sendFinish = false;
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 911cac2..6bb7949 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -189,8 +189,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mDnsServers[1] = DNS_DEFAULT_SERVER2;
}
- public void interfaceLinkStatusChanged(String iface, boolean link) {
- if (DEBUG) Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
+ public void interfaceStatusChanged(String iface, boolean up) {
+ if (DEBUG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
boolean found = false;
boolean usb = false;
if (isWifi(iface)) {
@@ -205,7 +205,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
synchronized (mIfaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
- if (link) {
+ if (up) {
if (sm == null) {
sm = new TetherInterfaceSM(iface, mLooper, usb);
mIfaces.put(iface, sm);
@@ -220,6 +220,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
+ public void interfaceLinkStateChanged(String iface, boolean up) {
+ if (DEBUG) Log.d(TAG, "interfaceLinkStateChanged " + iface + ", " + up);
+ interfaceStatusChanged(iface, up);
+ }
+
private boolean isUsb(String iface) {
for (String regex : mTetherableUsbRegexs) {
if (iface.matches(regex)) return true;
@@ -689,6 +694,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return retVal;
}
+ //TODO: Temporary handling upstream change triggered without
+ // CONNECTIVITY_ACTION. Only to accomodate interface
+ // switch during HO.
+ // @see bug/4455071
+ public void handleTetherIfaceChange() {
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+ }
+
class TetherInterfaceSM extends StateMachine {
// notification from the master SM that it's not in tether mode
static final int CMD_TETHER_MODE_DEAD = 1;
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 47813f8..c185012 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -27,15 +27,24 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.net.INetworkManagementEventObserver;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.server.ConnectivityService.VpnCallback;
+import java.io.OutputStream;
+import java.nio.charset.Charsets;
+import java.util.Arrays;
+
/**
* @hide
*/
@@ -47,9 +56,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
private final Context mContext;
private final VpnCallback mCallback;
- private String mPackageName;
- private String mInterfaceName;
- private String mDnsPropertyPrefix;
+ private String mPackage = VpnConfig.LEGACY_VPN;
+ private String mInterface;
+ private LegacyVpnRunner mLegacyVpnRunner;
public Vpn(Context context, VpnCallback callback) {
mContext = context;
@@ -57,74 +66,98 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
}
/**
- * Prepare for a VPN application.
+ * Protect a socket from routing changes by binding it to the given
+ * interface. The socket IS closed by this method.
+ *
+ * @param socket The socket to be bound.
+ * @param name The name of the interface.
+ */
+ public void protect(ParcelFileDescriptor socket, String interfaze) {
+ try {
+ mContext.enforceCallingPermission(VPN, "protect");
+ jniProtect(socket.getFd(), interfaze);
+ } finally {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Prepare for a VPN application. This method is designed to solve
+ * race conditions. It first compares the current prepared package
+ * with {@code oldPackage}. If they are the same, the prepared
+ * package is revoked and replaced with {@code newPackage}. If
+ * {@code oldPackage} is {@code null}, the comparison is omitted.
+ * If {@code newPackage} is the same package or {@code null}, the
+ * revocation is omitted. This method returns {@code true} if the
+ * operation is succeeded.
+ *
+ * Legacy VPN is handled specially since it is not a real package.
+ * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
+ * it can be revoked by itself.
*
- * @param packageName The package name of the new VPN application.
- * @return The name of the current prepared package.
+ * @param oldPackage The package name of the old VPN application.
+ * @param newPackage The package name of the new VPN application.
+ * @return true if the operation is succeeded.
*/
- public synchronized String prepare(String packageName) {
- // Return the current prepared package if the new one is null.
- if (packageName == null) {
- return mPackageName;
+ public synchronized boolean prepare(String oldPackage, String newPackage) {
+ // Return false if the package does not match.
+ if (oldPackage != null && !oldPackage.equals(mPackage)) {
+ return false;
}
- // Check the permission of the caller.
- PackageManager pm = mContext.getPackageManager();
- VpnConfig.enforceCallingPackage(pm.getNameForUid(Binder.getCallingUid()));
+ // Return true if we do not need to revoke.
+ if (newPackage == null ||
+ (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
+ return true;
+ }
+
+ // Only system user can revoke a package.
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Unauthorized Caller");
+ }
// Check the permission of the given package.
- if (packageName.isEmpty()) {
- packageName = null;
- } else if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(packageName + " does not have " + VPN);
+ PackageManager pm = mContext.getPackageManager();
+ if (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
+ pm.checkPermission(VPN, newPackage) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(newPackage + " does not have " + VPN);
}
// Reset the interface and hide the notification.
- if (mInterfaceName != null) {
- nativeReset(mInterfaceName);
+ if (mInterface != null) {
+ jniReset(mInterface);
mCallback.restore();
hideNotification();
- mInterfaceName = null;
+ mInterface = null;
}
- // Notify the package being revoked.
- if (mPackageName != null) {
+ // Send out the broadcast or stop LegacyVpnRunner.
+ if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
Intent intent = new Intent(VpnConfig.ACTION_VPN_REVOKED);
- intent.setPackage(mPackageName);
+ intent.setPackage(mPackage);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcast(intent);
+ } else if (mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.exit();
+ mLegacyVpnRunner = null;
}
- Log.i(TAG, "Switched from " + mPackageName + " to " + packageName);
- mPackageName = packageName;
- return mPackageName;
- }
-
- /**
- * Protect a socket from routing changes by binding it to the given
- * interface. The socket IS closed by this method.
- *
- * @param socket The socket to be bound.
- * @param name The name of the interface.
- */
- public void protect(ParcelFileDescriptor socket, String name) {
- try {
- mContext.enforceCallingPermission(VPN, "protect");
- nativeProtect(socket.getFd(), name);
- } finally {
- try {
- socket.close();
- } catch (Exception e) {
- // ignore
- }
- }
+ Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
+ mPackage = newPackage;
+ return true;
}
/**
- * Configure a TUN interface and return its file descriptor.
+ * Establish a VPN network and return the file descriptor of the VPN
+ * interface. This methods returns {@code null} if the application is
+ * revoked or not prepared.
*
- * @param configuration The parameters to configure the interface.
- * @return The file descriptor of the interface.
+ * @param config The parameters to configure the network.
+ * @return The file descriptor of the VPN interface.
*/
public synchronized ParcelFileDescriptor establish(VpnConfig config) {
// Check the permission of the caller.
@@ -134,7 +167,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
PackageManager pm = mContext.getPackageManager();
ApplicationInfo app = null;
try {
- app = pm.getApplicationInfo(mPackageName, 0);
+ app = pm.getApplicationInfo(mPackage, 0);
} catch (Exception e) {
return null;
}
@@ -142,85 +175,91 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
return null;
}
- // Create and configure the interface.
- ParcelFileDescriptor descriptor = ParcelFileDescriptor.adoptFd(
- nativeEstablish(config.mtu, config.addresses, config.routes));
+ // Load the label.
+ String label = app.loadLabel(pm).toString();
+
+ // Load the icon and convert it into a bitmap.
+ Drawable icon = app.loadIcon(pm);
+ Bitmap bitmap = null;
+ if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
+ int width = mContext.getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width);
+ int height = mContext.getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_height);
+ icon.setBounds(0, 0, width, height);
+ bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ icon.draw(new Canvas(bitmap));
+ }
- // Replace the interface and abort if it fails.
+ // Configure the interface. Abort if any of these steps fails.
+ ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(
+ jniConfigure(config.mtu, config.addresses, config.routes));
try {
- String interfaceName = nativeGetName(descriptor.getFd());
-
- if (mInterfaceName != null && !mInterfaceName.equals(interfaceName)) {
- nativeReset(mInterfaceName);
+ String interfaze = jniGetName(tun.getFd());
+ if (mInterface != null && !mInterface.equals(interfaze)) {
+ jniReset(mInterface);
}
- mInterfaceName = interfaceName;
+ mInterface = interfaze;
} catch (RuntimeException e) {
try {
- descriptor.close();
+ tun.close();
} catch (Exception ex) {
// ignore
}
throw e;
}
- String dnsServers = (config.dnsServers == null) ? "" : config.dnsServers.trim();
- mCallback.override(dnsServers.isEmpty() ? null : dnsServers.split(" "));
+ // Override DNS servers and search domains.
+ mCallback.override(config.dnsServers, config.searchDomains);
+
+ // Fill more values.
+ config.packagz = mPackage;
+ config.interfaze = mInterface;
- config.packageName = mPackageName;
- config.interfaceName = mInterfaceName;
- showNotification(pm, app, config);
- return descriptor;
+ // Show the notification!
+ showNotification(config, label, bitmap);
+ return tun;
}
// INetworkManagementEventObserver.Stub
- public void interfaceLinkStatusChanged(String name, boolean up) {
+ public void interfaceStatusChanged(String interfaze, boolean up) {
}
// INetworkManagementEventObserver.Stub
- public void interfaceAdded(String name) {
+ public void interfaceLinkStateChanged(String interfaze, boolean up) {
}
// INetworkManagementEventObserver.Stub
- public synchronized void interfaceRemoved(String name) {
- if (name.equals(mInterfaceName) && nativeCheck(name) == 0) {
- hideNotification();
- mInterfaceName = null;
+ public void interfaceAdded(String interfaze) {
+ }
+
+ // INetworkManagementEventObserver.Stub
+ public synchronized void interfaceRemoved(String interfaze) {
+ if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
mCallback.restore();
+ hideNotification();
+ mInterface = null;
}
}
- private void showNotification(PackageManager pm, ApplicationInfo app, VpnConfig config) {
+ private void showNotification(VpnConfig config, String label, Bitmap icon) {
NotificationManager nm = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (nm != null) {
- // Load the icon and convert it into a bitmap.
- Drawable icon = app.loadIcon(pm);
- Bitmap bitmap = null;
- if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
- int width = mContext.getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- int height = mContext.getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_height);
- icon.setBounds(0, 0, width, height);
- bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- icon.draw(new Canvas(bitmap));
- }
+ String title = (label == null) ? mContext.getString(R.string.vpn_title) :
+ mContext.getString(R.string.vpn_title_long, label);
+ String text = (config.session == null) ? mContext.getString(R.string.vpn_text) :
+ mContext.getString(R.string.vpn_text_long, config.session);
+ config.startTime = SystemClock.elapsedRealtime();
- // Load the label.
- String label = app.loadLabel(pm).toString();
-
- // Build the notification.
- String text = (config.sessionName == null) ? mContext.getString(R.string.vpn_text) :
- mContext.getString(R.string.vpn_text_long, config.sessionName);
long identity = Binder.clearCallingIdentity();
Notification notification = new Notification.Builder(mContext)
.setSmallIcon(R.drawable.vpn_connected)
- .setLargeIcon(bitmap)
- .setTicker(mContext.getString(R.string.vpn_ticker, label))
- .setContentTitle(mContext.getString(R.string.vpn_title, label))
+ .setLargeIcon(icon)
+ .setContentTitle(title)
.setContentText(text)
- .setContentIntent(VpnConfig.getIntentForNotification(mContext, config))
+ .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config))
.setDefaults(Notification.DEFAULT_ALL)
.setOngoing(true)
.getNotification();
@@ -240,9 +279,264 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
}
}
- private native int nativeEstablish(int mtu, String addresses, String routes);
- private native String nativeGetName(int fd);
- private native void nativeReset(String name);
- private native int nativeCheck(String name);
- private native void nativeProtect(int fd, String name);
+ private native int jniConfigure(int mtu, String addresses, String routes);
+ private native String jniGetName(int tun);
+ private native void jniReset(String interfaze);
+ private native int jniCheck(String interfaze);
+ private native void jniProtect(int socket, String interfaze);
+
+ /**
+ * Start legacy VPN. This method stops the daemons and restart them
+ * if arguments are not null. Heavy things are offloaded to another
+ * thread, so callers will not be blocked for a long time.
+ *
+ * @param config The parameters to configure the network.
+ * @param raoocn The arguments to be passed to racoon.
+ * @param mtpd The arguments to be passed to mtpd.
+ */
+ public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+ // Prepare for the new request. This also checks the caller.
+ prepare(null, VpnConfig.LEGACY_VPN);
+
+ // Start a new LegacyVpnRunner and we are done!
+ mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
+ mLegacyVpnRunner.start();
+ }
+
+ /**
+ * Return the information of the current ongoing legacy VPN.
+ */
+ public synchronized LegacyVpnInfo getLegacyVpnInfo() {
+ // Only system user can call this method.
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Unauthorized Caller");
+ }
+ return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo();
+ }
+
+ /**
+ * Bringing up a VPN connection takes time, and that is all this thread
+ * does. Here we have plenty of time. The only thing we need to take
+ * care of is responding to interruptions as soon as possible. Otherwise
+ * requests will be piled up. This can be done in a Handler as a state
+ * machine, but it is much easier to read in the current form.
+ */
+ private class LegacyVpnRunner extends Thread {
+ private static final String TAG = "LegacyVpnRunner";
+ private static final String NONE = "--";
+
+ private final VpnConfig mConfig;
+ private final String[] mDaemons;
+ private final String[][] mArguments;
+ private final LegacyVpnInfo mInfo;
+
+ private long mTimer = -1;
+
+ public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
+ super(TAG);
+ mConfig = config;
+ mDaemons = new String[] {"racoon", "mtpd"};
+ mArguments = new String[][] {racoon, mtpd};
+ mInfo = new LegacyVpnInfo();
+
+ // Legacy VPN is not a real package, so we use it to carry the key.
+ mInfo.key = mConfig.packagz;
+ mConfig.packagz = VpnConfig.LEGACY_VPN;
+ }
+
+ public void exit() {
+ // We assume that everything is reset after the daemons die.
+ for (String daemon : mDaemons) {
+ SystemProperties.set("ctl.stop", daemon);
+ }
+ interrupt();
+ }
+
+ public LegacyVpnInfo getInfo() {
+ // Update the info when VPN is disconnected.
+ if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) {
+ mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
+ mInfo.intent = null;
+ }
+ return mInfo;
+ }
+
+ @Override
+ public void run() {
+ // Wait for the previous thread since it has been interrupted.
+ Log.v(TAG, "Waiting");
+ synchronized (TAG) {
+ Log.v(TAG, "Executing");
+ execute();
+ }
+ }
+
+ private void checkpoint(boolean yield) throws InterruptedException {
+ long now = SystemClock.elapsedRealtime();
+ if (mTimer == -1) {
+ mTimer = now;
+ Thread.sleep(1);
+ } else if (now - mTimer <= 30000) {
+ Thread.sleep(yield ? 200 : 1);
+ } else {
+ mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
+ throw new IllegalStateException("time is up");
+ }
+ }
+
+ private void execute() {
+ // Catch all exceptions so we can clean up few things.
+ try {
+ // Initialize the timer.
+ checkpoint(false);
+ mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
+
+ // First stop the daemons.
+ for (String daemon : mDaemons) {
+ SystemProperties.set("ctl.stop", daemon);
+ }
+
+ // Wait for the daemons to stop.
+ for (String daemon : mDaemons) {
+ String key = "init.svc." + daemon;
+ while (!"stopped".equals(SystemProperties.get(key))) {
+ checkpoint(true);
+ }
+ }
+
+ // Reset the properties.
+ SystemProperties.set("vpn.dns", NONE);
+ SystemProperties.set("vpn.via", NONE);
+ while (!NONE.equals(SystemProperties.get("vpn.dns")) ||
+ !NONE.equals(SystemProperties.get("vpn.via"))) {
+ checkpoint(true);
+ }
+
+ // Check if we need to restart any of the daemons.
+ boolean restart = false;
+ for (String[] arguments : mArguments) {
+ restart = restart || (arguments != null);
+ }
+ if (!restart) {
+ mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
+ return;
+ }
+ mInfo.state = LegacyVpnInfo.STATE_CONNECTING;
+
+ // Start the daemon with arguments.
+ for (int i = 0; i < mDaemons.length; ++i) {
+ String[] arguments = mArguments[i];
+ if (arguments == null) {
+ continue;
+ }
+
+ // Start the daemon.
+ String daemon = mDaemons[i];
+ SystemProperties.set("ctl.start", daemon);
+
+ // Wait for the daemon to start.
+ String key = "init.svc." + daemon;
+ while (!"running".equals(SystemProperties.get(key))) {
+ checkpoint(true);
+ }
+
+ // Create the control socket.
+ LocalSocket socket = new LocalSocket();
+ LocalSocketAddress address = new LocalSocketAddress(
+ daemon, LocalSocketAddress.Namespace.RESERVED);
+
+ // Wait for the socket to connect.
+ while (true) {
+ try {
+ socket.connect(address);
+ break;
+ } catch (Exception e) {
+ // ignore
+ }
+ checkpoint(true);
+ }
+ socket.setSoTimeout(500);
+
+ // Send over the arguments.
+ OutputStream out = socket.getOutputStream();
+ for (String argument : arguments) {
+ byte[] bytes = argument.getBytes(Charsets.UTF_8);
+ if (bytes.length >= 0xFFFF) {
+ throw new IllegalArgumentException("argument is too large");
+ }
+ out.write(bytes.length >> 8);
+ out.write(bytes.length);
+ out.write(bytes);
+ checkpoint(false);
+ }
+
+ // Send End-Of-Arguments.
+ out.write(0xFF);
+ out.write(0xFF);
+ out.flush();
+ socket.close();
+ }
+
+ // Now here is the beast from the old days. We check few
+ // properties to figure out the current status. Ideally we
+ // can read things back from the sockets and get rid of the
+ // properties, but we have no time...
+ while (NONE.equals(SystemProperties.get("vpn.dns")) ||
+ NONE.equals(SystemProperties.get("vpn.via"))) {
+
+ // Check if a running daemon is dead.
+ for (int i = 0; i < mDaemons.length; ++i) {
+ String daemon = mDaemons[i];
+ if (mArguments[i] != null && !"running".equals(
+ SystemProperties.get("init.svc." + daemon))) {
+ throw new IllegalStateException(daemon + " is dead");
+ }
+ }
+ checkpoint(true);
+ }
+
+ // Now we are connected. Get the interface.
+ mConfig.interfaze = SystemProperties.get("vpn.via");
+
+ // Get the DNS servers if they are not set in config.
+ if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
+ String dnsServers = SystemProperties.get("vpn.dns").trim();
+ if (!dnsServers.isEmpty()) {
+ mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
+ }
+ }
+
+ // TODO: support search domains from ISAKMP mode config.
+
+ // The final step must be synchronized.
+ synchronized (Vpn.this) {
+ // Check if the thread is interrupted while we are waiting.
+ checkpoint(false);
+
+ // Check if the interface is gone while we are waiting.
+ if (jniCheck(mConfig.interfaze) == 0) {
+ throw new IllegalStateException(mConfig.interfaze + " is gone");
+ }
+
+ // Now INetworkManagementEventObserver is watching our back.
+ mInterface = mConfig.interfaze;
+ mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
+ showNotification(mConfig, null, null);
+
+ Log.i(TAG, "Connected!");
+ mInfo.state = LegacyVpnInfo.STATE_CONNECTED;
+ mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null);
+ }
+ } catch (Exception e) {
+ Log.i(TAG, "Aborting", e);
+ exit();
+ } finally {
+ // Do not leave an unstable state.
+ if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
+ mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
+ mInfo.state = LegacyVpnInfo.STATE_FAILED;
+ }
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 67e73f5..c813d37 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -32,7 +32,6 @@ import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.net.SntpClient;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -51,6 +50,7 @@ import android.telephony.SmsMessage;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
+import android.util.NtpTrustedTime;
import android.util.SparseIntArray;
import com.android.internal.app.IBatteryStats;
@@ -61,7 +61,7 @@ import com.android.internal.telephony.Phone;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
-import java.io.StringBufferInputStream;
+import java.io.StringReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map.Entry;
@@ -132,7 +132,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final int GPS_CAPABILITY_MSB = 0x0000002;
private static final int GPS_CAPABILITY_MSA = 0x0000004;
private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
-
+ private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
// these need to match AGpsType enum in gps.h
private static final int AGPS_TYPE_SUPL = 1;
@@ -200,6 +200,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
private boolean mInjectNtpTimePending = true;
private boolean mDownloadXtraDataPending = true;
+ // set to true if the GPS engine does not do on-demand NTP time requests
+ private boolean mPeriodicTimeInjection;
+
// true if GPS is navigating
private boolean mNavigating;
@@ -232,13 +235,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
// properties loaded from PROPERTIES_FILE
private Properties mProperties;
- private String mNtpServer;
private String mSuplServerHost;
private int mSuplServerPort;
private String mC2KServerHost;
private int mC2KServerPort;
private final Context mContext;
+ private final NtpTrustedTime mNtpTime;
private final ILocationManager mLocationManager;
private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
private Bundle mLocationExtras = new Bundle();
@@ -283,10 +286,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
// current setting - 5 minutes
private static final long RETRY_INTERVAL = 5*60*1000;
- // to avoid injecting bad NTP time, we reject any time fixes that differ from system time
- // by more than 5 minutes.
- private static final long MAX_NTP_SYSTEM_TIME_OFFSET = 5*60*1000;
-
private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
if (listener == null) {
@@ -375,6 +374,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
public GpsLocationProvider(Context context, ILocationManager locationManager) {
mContext = context;
+ mNtpTime = NtpTrustedTime.getInstance(context);
mLocationManager = locationManager;
mNIHandler = new GpsNetInitiatedHandler(context);
@@ -415,7 +415,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
FileInputStream stream = new FileInputStream(file);
mProperties.load(stream);
stream.close();
- mNtpServer = mProperties.getProperty("NTP_SERVER", null);
mSuplServerHost = mProperties.getProperty("SUPL_HOST");
String portString = mProperties.getProperty("SUPL_PORT");
@@ -527,13 +526,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
mInjectNtpTimePending = false;
- SntpClient client = new SntpClient();
long delay;
- if (client.requestTime(mNtpServer, 10000)) {
- long time = client.getNtpTime();
- long timeReference = client.getNtpTimeReference();
- int certainty = (int)(client.getRoundTripTime()/2);
+ // force refresh NTP cache when outdated
+ if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
+ mNtpTime.forceRefresh();
+ }
+
+ // only update when NTP time is fresh
+ if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
+ long time = mNtpTime.getCachedNtpTime();
+ long timeReference = mNtpTime.getCachedNtpTimeReference();
+ long certainty = mNtpTime.getCacheCertainty();
long now = System.currentTimeMillis();
Log.d(TAG, "NTP server returned: "
@@ -542,17 +546,19 @@ public class GpsLocationProvider implements LocationProviderInterface {
+ " certainty: " + certainty
+ " system time offset: " + (time - now));
- native_inject_time(time, timeReference, certainty);
+ native_inject_time(time, timeReference, (int) certainty);
delay = NTP_INTERVAL;
} else {
if (DEBUG) Log.d(TAG, "requestTime failed");
delay = RETRY_INTERVAL;
}
- // send delayed message for next NTP injection
- // since this is delayed and not urgent we do not hold a wake lock here
- mHandler.removeMessages(INJECT_NTP_TIME);
- mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+ if (mPeriodicTimeInjection) {
+ // send delayed message for next NTP injection
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.removeMessages(INJECT_NTP_TIME);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+ }
}
private void handleDownloadXtraData() {
@@ -1305,6 +1311,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
*/
private void setEngineCapabilities(int capabilities) {
mEngineCapabilities = capabilities;
+
+ if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) {
+ mPeriodicTimeInjection = true;
+ requestUtcTime();
+ }
}
/**
@@ -1385,7 +1396,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
Properties extraProp = new Properties();
try {
- extraProp.load(new StringBufferInputStream(extras));
+ extraProp.load(new StringReader(extras));
}
catch (IOException e)
{
@@ -1438,6 +1449,14 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
/**
+ * Called from native code to request utc time info
+ */
+
+ private void requestUtcTime() {
+ sendMessage(INJECT_NTP_TIME, 0, null);
+ }
+
+ /**
* Called from native code to request reference location info
*/
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 584cd03..d23d0f4 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.READ_PHONE_STATE;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.*;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -57,6 +58,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
@@ -71,7 +74,9 @@ import android.net.NetworkTemplate;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.INetworkManagementService;
import android.os.IPowerManager;
+import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
@@ -108,6 +113,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import libcore.io.IoUtils;
@@ -148,10 +154,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
+ private static final int MSG_RULES_CHANGED = 0x1;
+ private static final int MSG_METERED_IFACES_CHANGED = 0x2;
+
private final Context mContext;
private final IActivityManager mActivityManager;
private final IPowerManager mPowerManager;
private final INetworkStatsService mNetworkStats;
+ private final INetworkManagementService mNetworkManagement;
private final TrustedTime mTime;
private IConnectivityManager mConnManager;
@@ -160,6 +170,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private final Object mRulesLock = new Object();
private boolean mScreenOn;
+ private boolean mBackgroundData;
/** Current policy for network templates. */
private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList();
@@ -188,11 +199,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// TODO: keep whitelist of system-critical services that should never have
// rules enforced, such as system, phone, and radio UIDs.
+ // TODO: watch for package added broadcast to catch new UIDs.
+
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
- IPowerManager powerManager, INetworkStatsService networkStats) {
- // TODO: move to using cached NtpTrustedTime
- this(context, activityManager, powerManager, networkStats, new NtpTrustedTime(),
- getSystemDir());
+ IPowerManager powerManager, INetworkStatsService networkStats,
+ INetworkManagementService networkManagement) {
+ this(context, activityManager, powerManager, networkStats, networkManagement,
+ NtpTrustedTime.getInstance(context), getSystemDir());
}
private static File getSystemDir() {
@@ -200,17 +213,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
- IPowerManager powerManager, INetworkStatsService networkStats, TrustedTime time,
- File systemDir) {
+ IPowerManager powerManager, INetworkStatsService networkStats,
+ INetworkManagementService networkManagement,
+ TrustedTime time, File systemDir) {
mContext = checkNotNull(context, "missing context");
mActivityManager = checkNotNull(activityManager, "missing activityManager");
mPowerManager = checkNotNull(powerManager, "missing powerManager");
mNetworkStats = checkNotNull(networkStats, "missing networkStats");
+ mNetworkManagement = checkNotNull(networkManagement, "missing networkManagement");
mTime = checkNotNull(time, "missing TrustedTime");
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
}
@@ -231,6 +246,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
updateScreenOn();
+ updateBackgroundData(true);
try {
mActivityManager.registerProcessObserver(mProcessObserver);
@@ -256,11 +272,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
- // listen for warning polling events; currently dispatched by
+ // listen for stats update events
final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
mContext.registerReceiver(
mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
+ // listen for changes to background data flag
+ final IntentFilter bgFilter = new IntentFilter(ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+ mContext.registerReceiver(mBgReceiver, bgFilter, CONNECTIVITY_INTERNAL, mHandler);
+
}
private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@@ -269,9 +289,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// only someone like AMS should only be calling us
mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
- // skip when UID couldn't have any policy
- if (!isUidValidForPolicy(mContext, uid)) return;
-
synchronized (mRulesLock) {
// because a uid can have multiple pids running inside, we need to
// remember all pid states and summarize foreground at uid level.
@@ -292,9 +309,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// only someone like AMS should only be calling us
mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
- // skip when UID couldn't have any policy
- if (!isUidValidForPolicy(mContext, uid)) return;
-
synchronized (mRulesLock) {
// clear records and recompute, when they exist
final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
@@ -349,6 +363,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
};
/**
+ * Receiver that watches for
+ * {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
+ */
+ private BroadcastReceiver mBgReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and verified CONNECTIVITY_INTERNAL
+ // permission above.
+
+ synchronized (mRulesLock) {
+ updateBackgroundData(false);
+ }
+ }
+ };
+
+ /**
* Check {@link NetworkPolicy} against current {@link INetworkStatsService}
* to show visible notifications as needed.
*/
@@ -561,7 +591,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
- mMeteredIfaces.clear();
+ final HashSet<String> newMeteredIfaces = Sets.newHashSet();
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
@@ -591,28 +621,30 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
// remaining "quota" is based on usage in current cycle
final long quota = Math.max(0, policy.limitBytes - total);
- //kernelSetIfacesQuota(ifaces, quota);
+
+ if (ifaces.length > 1) {
+ // TODO: switch to shared quota once NMS supports
+ Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
+ }
for (String iface : ifaces) {
- mMeteredIfaces.add(iface);
+ removeInterfaceQuota(iface);
+ setInterfaceQuota(iface, quota);
+ newMeteredIfaces.add(iface);
}
}
}
- // dispatch changed rule to existing listeners
- // TODO: dispatch outside of holding lock
- final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
- final int length = mListeners.beginBroadcast();
- for (int i = 0; i < length; i++) {
- final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
- if (listener != null) {
- try {
- listener.onMeteredIfacesChanged(meteredIfaces);
- } catch (RemoteException e) {
- }
+ // remove quota on any trailing interfaces
+ for (String iface : mMeteredIfaces) {
+ if (!newMeteredIfaces.contains(iface)) {
+ removeInterfaceQuota(iface);
}
}
- mListeners.finishBroadcast();
+ mMeteredIfaces = newMeteredIfaces;
+
+ final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
+ mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget();
}
/**
@@ -804,32 +836,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mListeners.register(listener);
- synchronized (mRulesLock) {
- // dispatch any existing rules to new listeners
- // TODO: dispatch outside of holding lock
- final int size = mUidRules.size();
- for (int i = 0; i < size; i++) {
- final int uid = mUidRules.keyAt(i);
- final int uidRules = mUidRules.valueAt(i);
- if (uidRules != RULE_ALLOW_ALL) {
- try {
- listener.onUidRulesChanged(uid, uidRules);
- } catch (RemoteException e) {
- }
- }
- }
-
- // dispatch any metered ifaces to new listeners
- // TODO: dispatch outside of holding lock
- if (mMeteredIfaces.size() > 0) {
- final String[] meteredIfaces = mMeteredIfaces.toArray(
- new String[mMeteredIfaces.size()]);
- try {
- listener.onMeteredIfacesChanged(meteredIfaces);
- } catch (RemoteException e) {
- }
- }
- }
+ // TODO: consider dispatching existing rules to new listeners
}
@Override
@@ -963,6 +970,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ private void updateBackgroundData(boolean systemReady) {
+ synchronized (mRulesLock) {
+ try {
+ mBackgroundData = mConnManager.getBackgroundDataSetting();
+ } catch (RemoteException e) {
+ }
+ if (systemReady && mBackgroundData) {
+ // typical behavior of background enabled during systemReady;
+ // no need to clear rules for all UIDs.
+ } else {
+ updateRulesForBackgroundDataLocked();
+ }
+ }
+ }
+
/**
* Update rules that might be changed by {@link #mScreenOn} value.
*/
@@ -977,9 +999,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
- private void updateRulesForUidLocked(int uid) {
- if (!isUidValidForPolicy(mContext, uid)) return;
+ /**
+ * Update rules that might be changed by {@link #mBackgroundData} value.
+ */
+ private void updateRulesForBackgroundDataLocked() {
+ // update rules for all installed applications
+ final PackageManager pm = mContext.getPackageManager();
+ final List<ApplicationInfo> apps = pm.getInstalledApplications(0);
+ for (ApplicationInfo app : apps) {
+ updateRulesForUidLocked(app.uid);
+ }
+ // and catch system UIDs
+ // TODO: keep in sync with android_filesystem_config.h
+ for (int uid = 1000; uid <= 1025; uid++) {
+ updateRulesForUidLocked(uid);
+ }
+ for (int uid = 2000; uid <= 2002; uid++) {
+ updateRulesForUidLocked(uid);
+ }
+ for (int uid = 3000; uid <= 3007; uid++) {
+ updateRulesForUidLocked(uid);
+ }
+ for (int uid = 9998; uid <= 9999; uid++) {
+ updateRulesForUidLocked(uid);
+ }
+ }
+
+ private void updateRulesForUidLocked(int uid) {
final int uidPolicy = getUidPolicy(uid);
final boolean uidForeground = isUidForeground(uid);
@@ -989,28 +1036,96 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// uid in background, and policy says to block metered data
uidRules = RULE_REJECT_METERED;
}
+ if (!uidForeground && !mBackgroundData) {
+ // uid in background, and global background disabled
+ uidRules = RULE_REJECT_METERED;
+ }
// TODO: only dispatch when rules actually change
- // record rule locally to dispatch to new listeners
- mUidRules.put(uid, uidRules);
+ if (uidRules == RULE_ALLOW_ALL) {
+ mUidRules.delete(uid);
+ } else {
+ mUidRules.put(uid, uidRules);
+ }
final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0;
- //kernelSetUidRejectPaid(uid, rejectPaid);
+ setUidNetworkRules(uid, rejectMetered);
// dispatch changed rule to existing listeners
- // TODO: dispatch outside of holding lock
- final int length = mListeners.beginBroadcast();
- for (int i = 0; i < length; i++) {
- final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
- if (listener != null) {
- try {
- listener.onUidRulesChanged(uid, uidRules);
- } catch (RemoteException e) {
+ mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget();
+ }
+
+ private Handler.Callback mHandlerCallback = new Handler.Callback() {
+ /** {@inheritDoc} */
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_RULES_CHANGED: {
+ final int uid = msg.arg1;
+ final int uidRules = msg.arg2;
+ final int length = mListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ if (listener != null) {
+ try {
+ listener.onUidRulesChanged(uid, uidRules);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mListeners.finishBroadcast();
+ return true;
+ }
+ case MSG_METERED_IFACES_CHANGED: {
+ final String[] meteredIfaces = (String[]) msg.obj;
+ final int length = mListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ if (listener != null) {
+ try {
+ listener.onMeteredIfacesChanged(meteredIfaces);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mListeners.finishBroadcast();
+ return true;
+ }
+ default: {
+ return false;
}
}
}
- mListeners.finishBroadcast();
+ };
+
+ private void setInterfaceQuota(String iface, long quota) {
+ try {
+ mNetworkManagement.setInterfaceQuota(iface, quota);
+ } catch (IllegalStateException e) {
+ Slog.e(TAG, "problem setting interface quota", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "problem setting interface quota", e);
+ }
+ }
+
+ private void removeInterfaceQuota(String iface) {
+ try {
+ mNetworkManagement.removeInterfaceQuota(iface);
+ } catch (IllegalStateException e) {
+ Slog.e(TAG, "problem removing interface quota", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "problem removing interface quota", e);
+ }
+ }
+
+ private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+ try {
+ mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+ } catch (IllegalStateException e) {
+ Slog.e(TAG, "problem setting uid rules", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "problem setting uid rules", e);
+ }
}
private String getActiveSubscriberId() {
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 524dd40..b6834f6 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -27,7 +27,6 @@ import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
-import static android.provider.Settings.Secure.NETSTATS_ENABLED;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
@@ -71,7 +70,6 @@ import android.util.Slog;
import android.util.TrustedTime;
import com.android.internal.os.AtomicFile;
-import com.android.server.NativeDaemonConnectorException;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
@@ -124,8 +122,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private PendingIntent mPollIntent;
// TODO: listen for kernel push events through netd instead of polling
- // TODO: watch for UID uninstall, and transfer stats into single bucket
-
// TODO: trim empty history objects entirely
private static final long KB_IN_BYTES = 1024;
@@ -136,7 +132,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* Settings that can be changed externally.
*/
public interface NetworkStatsSettings {
- public boolean getEnabled();
public long getPollInterval();
public long getPersistThreshold();
public long getNetworkBucketDuration();
@@ -178,9 +173,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public NetworkStatsService(
Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
- // TODO: move to using cached NtpTrustedTime
- this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir(),
- new DefaultNetworkStatsSettings(context));
+ this(context, networkManager, alarmManager, NtpTrustedTime.getInstance(context),
+ getSystemDir(), new DefaultNetworkStatsSettings(context));
}
private static File getSystemDir() {
@@ -209,20 +203,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
public void systemReady() {
- if (mSettings.getEnabled()) {
- try {
- // enable low-level bandwidth stats and control
- // TODO: consider shipping with this enabled by default
- mNetworkManager.setBandwidthControlEnabled(true);
- } catch (RemoteException e) {
- Slog.e(TAG, "problem talking to netd while enabling bandwidth controls", e);
- } catch (NativeDaemonConnectorException ndce) {
- Slog.e(TAG, "problem enabling bandwidth controls", ndce);
- }
- } else {
- Slog.w(TAG, "detailed network stats disabled");
- }
-
synchronized (mStatsLock) {
// read historical network stats from disk, since policy service
// might need them right away. we delay loading detailed UID stats
@@ -391,6 +371,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
+ @Override
+ public void forceUpdate() {
+ mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+
+ synchronized (mStatsLock) {
+ performPollLocked(true, false);
+ }
+ }
+
/**
* Receiver that watches for {@link IConnectivityManager} to claim network
* interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
@@ -506,8 +495,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
try {
networkSnapshot = mNetworkManager.getNetworkStatsSummary();
uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null;
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "problem reading network stats: " + e);
+ return;
} catch (RemoteException e) {
- Slog.w(TAG, "problem reading network stats");
+ Slog.w(TAG, "problem reading network stats: " + e);
return;
}
@@ -904,6 +896,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
argSet.add(arg);
}
+ final boolean fullHistory = argSet.contains("full");
+
synchronized (mStatsLock) {
// TODO: remove this testing code, since it corrupts stats
if (argSet.contains("generate")) {
@@ -929,7 +923,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
final NetworkStatsHistory history = mNetworkStats.get(ident);
pw.print(" ident="); pw.println(ident.toString());
- history.dump(" ", pw);
+ history.dump(" ", pw, fullHistory);
}
if (argSet.contains("detail")) {
@@ -949,7 +943,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStatsHistory history = uidStats.valueAt(i);
pw.print(" UID="); pw.print(uid);
pw.print(" tag="); pw.println(tag);
- history.dump(" ", pw);
+ history.dump(" ", pw, fullHistory);
}
}
}
@@ -1057,13 +1051,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return Settings.Secure.getLong(mResolver, name, def);
}
- public boolean getEnabled() {
- if (!new File("/proc/net/xt_qtaguid/ctrl").exists()) {
- Slog.w(TAG, "kernel does not support bandwidth control");
- return false;
- }
- return Settings.Secure.getInt(mResolver, NETSTATS_ENABLED, 1) != 0;
- }
public long getPollInterval() {
return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
}
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index d10aa97..11ccd60 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -307,7 +307,7 @@ class Installer {
}
public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath,
- PackageStats pStats) {
+ String asecPath, PackageStats pStats) {
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
@@ -315,17 +315,20 @@ class Installer {
builder.append(apkPath);
builder.append(' ');
builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
+ builder.append(' ');
+ builder.append(asecPath != null ? asecPath : "!");
String s = transaction(builder.toString());
String res[] = s.split(" ");
- if ((res == null) || (res.length != 4)) {
+ if ((res == null) || (res.length != 5)) {
return -1;
}
try {
pStats.codeSize = Long.parseLong(res[1]);
pStats.dataSize = Long.parseLong(res[2]);
pStats.cacheSize = Long.parseLong(res[3]);
+ pStats.externalCodeSize = Long.parseLong(res[4]);
return Integer.parseInt(res[0]);
} catch (NumberFormatException e) {
return -1;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 5a9dae9..d6a15e6 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -75,6 +75,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
import android.os.FileUtils;
+import android.os.FileUtils.FileStatus;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -156,7 +157,6 @@ public class PackageManagerService extends IPackageManager.Stub {
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 KEYCHAIN_UID = Process.KEYCHAIN_UID;
static final int FIRST_APPLICATION_UID =
Process.FIRST_APPLICATION_UID;
static final int MAX_APPLICATION_UIDS = 1000;
@@ -760,10 +760,6 @@ public class PackageManagerService extends IPackageManager.Stub {
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) {
@@ -782,16 +778,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mSeparateProcesses = null;
}
- Installer installer = new Installer();
- // Little hacky thing to check if installd is here, to determine
- // whether we are running on the simulator and thus need to take
- // care of building the /data file structure ourself.
- // (apparently the sim now has a working installer)
- if (installer.ping() && Process.supportsProcesses()) {
- mInstaller = installer;
- } else {
- mInstaller = null;
- }
+ mInstaller = new Installer();
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Display d = wm.getDefaultDisplay();
@@ -810,17 +797,6 @@ public class PackageManagerService extends IPackageManager.Stub {
mUserManager = new UserManager(mInstaller, mUserAppDataDir);
- if (mInstaller == null) {
- // Make sure these dirs exist, when we are running in
- // the simulator.
- // Make a wide-open directory for random misc stuff.
- File miscDir = new File(dataDir, "misc");
- miscDir.mkdirs();
- mAppDataDir.mkdirs();
- mUserAppDataDir.mkdirs();
- mDrmAppPrivateInstallDir.mkdirs();
- }
-
readPermissions();
mRestoredSettings = mSettings.readLPw();
@@ -842,104 +818,102 @@ public class PackageManagerService extends IPackageManager.Stub {
mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
mDalvikCacheDir = new File(dataDir, "dalvik-cache");
- if (mInstaller != null) {
- boolean didDexOpt = false;
-
- /**
- * Out of paranoia, ensure that everything in the boot class
- * path has been dexed.
- */
- String bootClassPath = System.getProperty("java.boot.class.path");
- if (bootClassPath != null) {
- String[] paths = splitString(bootClassPath, ':');
- for (int i=0; i<paths.length; i++) {
- try {
- if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
- libFiles.add(paths[i]);
- mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
- didDexOpt = true;
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Boot class path not found: " + paths[i]);
- } catch (IOException e) {
- Slog.w(TAG, "Exception reading boot class path: " + paths[i], e);
+ boolean didDexOpt = false;
+
+ /**
+ * Out of paranoia, ensure that everything in the boot class
+ * path has been dexed.
+ */
+ String bootClassPath = System.getProperty("java.boot.class.path");
+ if (bootClassPath != null) {
+ String[] paths = splitString(bootClassPath, ':');
+ for (int i=0; i<paths.length; i++) {
+ try {
+ if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
+ libFiles.add(paths[i]);
+ mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
+ didDexOpt = true;
}
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Boot class path not found: " + paths[i]);
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception reading boot class path: " + paths[i], e);
}
- } else {
- Slog.w(TAG, "No BOOTCLASSPATH found!");
}
+ } else {
+ Slog.w(TAG, "No BOOTCLASSPATH found!");
+ }
- /**
- * Also ensure all external libraries have had dexopt run on them.
- */
- if (mSharedLibraries.size() > 0) {
- Iterator<String> libs = mSharedLibraries.values().iterator();
- while (libs.hasNext()) {
- String lib = libs.next();
- try {
- if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
- libFiles.add(lib);
- mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
- didDexOpt = true;
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Library not found: " + lib);
- } catch (IOException e) {
- Slog.w(TAG, "Exception reading library: " + lib, e);
+ /**
+ * Also ensure all external libraries have had dexopt run on them.
+ */
+ if (mSharedLibraries.size() > 0) {
+ Iterator<String> libs = mSharedLibraries.values().iterator();
+ while (libs.hasNext()) {
+ String lib = libs.next();
+ try {
+ if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
+ libFiles.add(lib);
+ mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
+ didDexOpt = true;
}
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Library not found: " + lib);
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception reading library: " + lib, e);
}
}
+ }
- // Gross hack for now: we know this file doesn't contain any
- // code, so don't dexopt it to avoid the resulting log spew.
- libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");
-
- /**
- * And there are a number of commands implemented in Java, which
- * we currently need to do the dexopt on so that they can be
- * run from a non-root shell.
- */
- String[] frameworkFiles = mFrameworkDir.list();
- if (frameworkFiles != null) {
- for (int i=0; i<frameworkFiles.length; i++) {
- File libPath = new File(mFrameworkDir, frameworkFiles[i]);
- String path = libPath.getPath();
- // Skip the file if we alrady did it.
- if (libFiles.contains(path)) {
- continue;
- }
- // Skip the file if it is not a type we want to dexopt.
- if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
- continue;
- }
- try {
- if (dalvik.system.DexFile.isDexOptNeeded(path)) {
- mInstaller.dexopt(path, Process.SYSTEM_UID, true);
- didDexOpt = true;
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Jar not found: " + path);
- } catch (IOException e) {
- Slog.w(TAG, "Exception reading jar: " + path, e);
+ // Gross hack for now: we know this file doesn't contain any
+ // code, so don't dexopt it to avoid the resulting log spew.
+ libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");
+
+ /**
+ * And there are a number of commands implemented in Java, which
+ * we currently need to do the dexopt on so that they can be
+ * run from a non-root shell.
+ */
+ String[] frameworkFiles = mFrameworkDir.list();
+ if (frameworkFiles != null) {
+ for (int i=0; i<frameworkFiles.length; i++) {
+ File libPath = new File(mFrameworkDir, frameworkFiles[i]);
+ String path = libPath.getPath();
+ // Skip the file if we alrady did it.
+ if (libFiles.contains(path)) {
+ continue;
+ }
+ // Skip the file if it is not a type we want to dexopt.
+ if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
+ continue;
+ }
+ try {
+ if (dalvik.system.DexFile.isDexOptNeeded(path)) {
+ mInstaller.dexopt(path, Process.SYSTEM_UID, true);
+ didDexOpt = true;
}
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Jar not found: " + path);
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception reading jar: " + path, e);
}
}
+ }
- if (didDexOpt) {
- // If we had to do a dexopt of one of the previous
- // things, then something on the system has changed.
- // Consider this significant, and wipe away all other
- // existing dexopt files to ensure we don't leave any
- // dangling around.
- String[] files = mDalvikCacheDir.list();
- if (files != null) {
- for (int i=0; i<files.length; i++) {
- String fn = files[i];
- if (fn.startsWith("data@app@")
- || fn.startsWith("data@app-private@")) {
- Slog.i(TAG, "Pruning dalvik file: " + fn);
- (new File(mDalvikCacheDir, fn)).delete();
- }
+ if (didDexOpt) {
+ // If we had to do a dexopt of one of the previous
+ // things, then something on the system has changed.
+ // Consider this significant, and wipe away all other
+ // existing dexopt files to ensure we don't leave any
+ // dangling around.
+ String[] files = mDalvikCacheDir.list();
+ if (files != null) {
+ for (int i=0; i<files.length; i++) {
+ String fn = files[i];
+ if (fn.startsWith("data@app@")
+ || fn.startsWith("data@app-private@")) {
+ Slog.i(TAG, "Pruning dalvik file: " + fn);
+ (new File(mDalvikCacheDir, fn)).delete();
}
}
}
@@ -969,11 +943,9 @@ public class PackageManagerService extends IPackageManager.Stub {
scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
- if (mInstaller != null) {
- if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
- mInstaller.moveFiles();
- }
-
+ if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
+ mInstaller.moveFiles();
+
// Prune any system packages that no longer exist.
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
@@ -985,19 +957,12 @@ public class PackageManagerService extends IPackageManager.Stub {
String msg = "System package " + ps.name
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
- if (mInstaller != null) {
- mInstaller.remove(ps.name, 0);
- mUserManager.removePackageForAllUsers(ps.name);
- }
+ mInstaller.remove(ps.name, 0);
+ mUserManager.removePackageForAllUsers(ps.name);
}
}
mAppInstallDir = new File(dataDir, "app");
- if (mInstaller == null) {
- // Make sure these dirs exist, when we are running in
- // the simulator.
- mAppInstallDir.mkdirs(); // scanDirLI() assumes this dir exists
- }
//look for any incomplete package installations
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
//clean up list
@@ -1071,19 +1036,12 @@ public 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, 0);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove app data directory for package: "
- + ps.name + ", retcode=" + retCode);
- } else {
- mUserManager.removePackageForAllUsers(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 {
- //for emulator
- PackageParser.Package pkg = mPackages.get(ps.name);
- File dataDir = new File(pkg.applicationInfo.dataDir);
- dataDir.delete();
+ mUserManager.removePackageForAllUsers(ps.name);
}
if (ps.codePath != null) {
if (!ps.codePath.delete()) {
@@ -1566,12 +1524,10 @@ public class PackageManagerService extends IPackageManager.Stub {
public void run() {
mHandler.removeCallbacks(this);
int retCode = -1;
- if (mInstaller != null) {
- retCode = mInstaller.freeCache(freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
- }
- } //end if mInstaller
+ retCode = mInstaller.freeCache(freeStorageSize);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't clear application caches");
+ }
if (observer != null) {
try {
observer.onRemoveCompleted(null, (retCode >= 0));
@@ -1591,11 +1547,9 @@ public class PackageManagerService extends IPackageManager.Stub {
public void run() {
mHandler.removeCallbacks(this);
int retCode = -1;
- if (mInstaller != null) {
- retCode = mInstaller.freeCache(freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
- }
+ retCode = mInstaller.freeCache(freeStorageSize);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't clear application caches");
}
if(pi != null) {
try {
@@ -2854,7 +2808,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) {
boolean performed = false;
- if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0 && mInstaller != null) {
+ if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
String path = pkg.mScanPath;
int ret = 0;
try {
@@ -3239,42 +3193,39 @@ public class PackageManagerService extends IPackageManager.Stub {
mOutPermissions[1] = 0;
FileUtils.getPermissions(dataPath.getPath(), mOutPermissions);
- // If we have mismatched owners for the data path, we have a
- // problem (unless we're running in the simulator.)
- if (mOutPermissions[1] != pkg.applicationInfo.uid && Process.supportsProcesses()) {
+ // If we have mismatched owners for the data path, we have a problem.
+ if (mOutPermissions[1] != pkg.applicationInfo.uid) {
boolean recovered = false;
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
// If this is a system app, we can at least delete its
// current data so the application will still work.
- if (mInstaller != null) {
- int ret = mInstaller.remove(pkgName, 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: "
- + mOutPermissions[1] + " to "
- + pkg.applicationInfo.uid + "; old data erased";
+ 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: "
+ + mOutPermissions[1] + " to "
+ + pkg.applicationInfo.uid + "; old data erased";
+ reportSettingsProblem(Log.WARN, msg);
+ recovered = true;
+
+ // And now re-install the app.
+ ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+ pkg.applicationInfo.uid);
+ if (ret == -1) {
+ // Ack should not happen!
+ msg = "System package " + pkg.packageName
+ + " could not have data directory re-created after delete.";
reportSettingsProblem(Log.WARN, msg);
- recovered = true;
-
- // And now re-install the app.
- ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.uid);
- if (ret == -1) {
- // Ack should not happen!
- msg = "System package " + pkg.packageName
- + " could not have data directory re-created after delete.";
- reportSettingsProblem(Log.WARN, msg);
- mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- return null;
- }
- // Create data directories for all users
- mUserManager.installPackageForAllUsers(pkgName,
- pkg.applicationInfo.uid);
+ mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName,
+ pkg.applicationInfo.uid);
}
if (!recovered) {
mHasSystemUidErrors = true;
@@ -3307,25 +3258,16 @@ public class PackageManagerService extends IPackageManager.Stub {
Log.v(TAG, "Want this data dir: " + dataPath);
}
//invoke installer to do the actual installation
- if (mInstaller != null) {
- int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.uid);
- 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()) {
- FileUtils.setPermissions(
- dataPath.toString(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
- pkg.applicationInfo.uid, pkg.applicationInfo.uid);
- }
+ int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+ pkg.applicationInfo.uid);
+ 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);
+
if (dataPath.exists()) {
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
@@ -3356,65 +3298,62 @@ public class PackageManagerService extends IPackageManager.Stub {
pkgSetting.uidError = uidError;
}
- // If we're running in the simulator, we don't need to unpack anything.
- if (mInstaller != null) {
- String path = scanFile.getPath();
- /* Note: We don't want to unpack the native binaries for
- * system applications, unless they have been updated
- * (the binaries are already under /system/lib).
- * Also, don't unpack libs for apps on the external card
- * since they should have their libraries in the ASEC
- * container already.
- *
- * In other words, we're going to unpack the binaries
- * only for non-system apps and system app upgrades.
- */
- if (pkg.applicationInfo.nativeLibraryDir != null) {
- try {
- final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
- final String dataPathString = dataPath.getCanonicalFile().getPath();
+ String path = scanFile.getPath();
+ /* Note: We don't want to unpack the native binaries for
+ * system applications, unless they have been updated
+ * (the binaries are already under /system/lib).
+ * Also, don't unpack libs for apps on the external card
+ * since they should have their libraries in the ASEC
+ * container already.
+ *
+ * In other words, we're going to unpack the binaries
+ * only for non-system apps and system app upgrades.
+ */
+ if (pkg.applicationInfo.nativeLibraryDir != null) {
+ 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);
- }
- } 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);
+ 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);
}
- } catch (IOException ioe) {
- Log.e(TAG, "Unable to get canonical file " + ioe.toString());
+ } 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);
}
+ } catch (IOException ioe) {
+ Log.e(TAG, "Unable to get canonical file " + ioe.toString());
}
- pkg.mScanPath = path;
+ }
+ pkg.mScanPath = path;
- if ((scanMode&SCAN_NO_DEX) == 0) {
- if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) {
- mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
- return null;
- }
+ if ((scanMode&SCAN_NO_DEX) == 0) {
+ if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) {
+ mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
+ return null;
}
}
@@ -4887,8 +4826,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private final IPackageStatsObserver mObserver;
- public MeasureParams(PackageStats stats, boolean success,
- IPackageStatsObserver observer) {
+ public MeasureParams(PackageStats stats, boolean success, IPackageStatsObserver observer) {
mObserver = observer;
mStats = stats;
mSuccess = success;
@@ -5439,7 +5377,7 @@ public class PackageManagerService extends IPackageManager.Stub {
void cleanUpResourcesLI() {
String sourceDir = getCodePath();
- if (cleanUp() && mInstaller != null) {
+ if (cleanUp()) {
int retCode = mInstaller.rmdex(sourceDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove dex file for package: "
@@ -5480,6 +5418,17 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ /**
+ * Extract the MountService "container ID" from the full code path of an
+ * .apk.
+ */
+ static String cidFromCodePath(String fullCodePath) {
+ int eidx = fullCodePath.lastIndexOf("/");
+ String subStr1 = fullCodePath.substring(0, eidx);
+ int sidx = subStr1.lastIndexOf("/");
+ return subStr1.substring(sidx+1, eidx);
+ }
+
class SdInstallArgs extends InstallArgs {
static final String RES_FILE_NAME = "pkg.apk";
@@ -5656,14 +5605,12 @@ public class PackageManagerService extends IPackageManager.Stub {
void cleanUpResourcesLI() {
String sourceFile = getCodePath();
// Remove dex file
- if (mInstaller != null) {
- int retCode = mInstaller.rmdex(sourceFile);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove dex file for package: "
- + " at location "
- + sourceFile.toString() + ", retcode=" + retCode);
- // we don't consider this to be a failure of the core package deletion
- }
+ int retCode = mInstaller.rmdex(sourceFile);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't remove dex file for package: "
+ + " at location "
+ + sourceFile.toString() + ", retcode=" + retCode);
+ // we don't consider this to be a failure of the core package deletion
}
cleanUp();
}
@@ -6071,9 +6018,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if((res.returnCode = setPermissionsLI(newPackage))
!= PackageManager.INSTALL_SUCCEEDED) {
- if (mInstaller != null) {
- mInstaller.rmdex(newPackage.mScanPath);
- }
+ mInstaller.rmdex(newPackage.mScanPath);
return;
} else {
Log.d(TAG, "New package installed in " + newPackage.mPath);
@@ -6201,15 +6146,8 @@ public class PackageManagerService extends IPackageManager.Stub {
} finally {
//TODO clean up the extracted public files
}
- if (mInstaller != null) {
- retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath),
- newPackage.applicationInfo.uid);
- } else {
- final int filePermissions =
- FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP;
- retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1,
- newPackage.applicationInfo.uid);
- }
+ retCode = mInstaller.setForwardLockPerm(getApkName(newPackage.mPath),
+ newPackage.applicationInfo.uid);
} else {
// The permissions on the resource file was set when it was copied for
// non forward locked apps and apps on sdcard
@@ -6472,25 +6410,14 @@ public class PackageManagerService extends IPackageManager.Stub {
deletedPs = mSettings.mPackages.get(packageName);
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
- if (mInstaller != null) {
- 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);
- }
+ 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 {
- // for simulator
- File dataDir;
- // reader
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(packageName);
- dataDir = new File(pkg.applicationInfo.dataDir);
- }
- dataDir.delete();
+ // TODO: Kill the processes first
+ mUserManager.removePackageForAllUsers(packageName);
}
schedulePackageCleaning(packageName);
}
@@ -6739,13 +6666,11 @@ public class PackageManagerService extends IPackageManager.Stub {
return false;
}
}
- if (mInstaller != null) {
- int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove cache files for package: "
- + packageName);
- return false;
- }
+ int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't remove cache files for package: "
+ + packageName);
+ return false;
}
return true;
}
@@ -6791,13 +6716,11 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
- if (mInstaller != null) {
- int retCode = mInstaller.deleteCacheFiles(packageName);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove cache files for package: "
- + packageName);
- return false;
- }
+ int retCode = mInstaller.deleteCacheFiles(packageName);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't remove cache files for package: "
+ + packageName);
+ return false;
}
return true;
}
@@ -6831,6 +6754,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
PackageParser.Package p;
boolean dataOnly = false;
+ String asecPath = null;
synchronized (mPackages) {
p = mPackages.get(packageName);
if(p == null) {
@@ -6842,6 +6766,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
p = ps.pkg;
}
+ if (p != null && isExternal(p)) {
+ String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir);
+ if (secureContainerId != null) {
+ asecPath = PackageHelper.getSdFilesystem(secureContainerId);
+ }
+ }
}
String publicSrcDir = null;
if(!dataOnly) {
@@ -6850,16 +6780,15 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
- publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null;
- }
- if (mInstaller != null) {
- int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, pStats);
- if (res < 0) {
- return false;
- } else {
- return true;
+ if (isForwardLocked(p)) {
+ publicSrcDir = applicationInfo.publicSourceDir;
}
}
+ int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir,
+ asecPath, pStats);
+ if (res < 0) {
+ return false;
+ }
return true;
}
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 918f1b6..1ab570a 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -45,7 +45,6 @@ import android.os.storage.StorageVolume;
import android.os.SystemProperties;
import android.os.UEventObserver;
import android.provider.Settings;
-import android.util.Log;
import android.util.Slog;
import java.io.File;
@@ -62,7 +61,7 @@ import java.util.List;
public class UsbDeviceManager {
private static final String TAG = UsbDeviceManager.class.getSimpleName();
- private static final boolean LOG = false;
+ private static final boolean DEBUG = false;
private static final String USB_STATE_MATCH =
"DEVPATH=/devices/virtual/android_usb/android0";
@@ -73,7 +72,9 @@ public class UsbDeviceManager {
private static final String STATE_PATH =
"/sys/class/android_usb/android0/state";
private static final String MASS_STORAGE_FILE_PATH =
- "/sys/class/android_usb/f_mass_storage/lun/file";
+ "/sys/class/android_usb/android0/f_mass_storage/lun/file";
+ private static final String RNDIS_ETH_ADDR_PATH =
+ "/sys/class/android_usb/android0/f_rndis/ethaddr";
private static final int MSG_UPDATE_STATE = 0;
private static final int MSG_ENABLE_ADB = 1;
@@ -93,18 +94,9 @@ public class UsbDeviceManager {
private final UsbSettingsManager mSettingsManager;
private NotificationManager mNotificationManager;
private final boolean mHasUsbAccessory;
-
- // for USB connected notification
- private boolean mUsbNotificationShown;
private boolean mUseUsbNotification;
- private Notification mUsbNotification;
-
- // for adb connected notification
- private boolean mAdbNotificationShown;
- private Notification mAdbNotification;
private boolean mAdbEnabled;
-
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
super(null);
@@ -117,115 +109,20 @@ public class UsbDeviceManager {
}
}
- private void updateUsbNotification(boolean connected) {
- if (mNotificationManager == null || !mUseUsbNotification) return;
- if (connected) {
- if (!mUsbNotificationShown) {
- Resources r = mContext.getResources();
- CharSequence title = r.getText(
- com.android.internal.R.string.usb_preferences_notification_title);
- CharSequence message = r.getText(
- com.android.internal.R.string.usb_preferece_notification_message);
-
- if (mUsbNotification == null) {
- mUsbNotification = new Notification();
- mUsbNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
- mUsbNotification.when = 0;
- mUsbNotification.flags = Notification.FLAG_ONGOING_EVENT;
- mUsbNotification.tickerText = title;
- mUsbNotification.defaults = 0; // please be quiet
- mUsbNotification.sound = null;
- mUsbNotification.vibrate = null;
- }
-
- Intent intent = new Intent();
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbPreferenceActivity");
- PendingIntent pi = PendingIntent.getActivity(mContext, 0,
- intent, 0);
-
- mUsbNotification.setLatestEventInfo(mContext, title, message, pi);
-
- mUsbNotificationShown = true;
- mNotificationManager.notify(
- com.android.internal.R.string.usb_preferences_notification_title,
- mUsbNotification);
- }
-
- } else if (mUsbNotificationShown) {
- mUsbNotificationShown = false;
- mNotificationManager.cancel(
- com.android.internal.R.string.usb_preferences_notification_title);
- }
- }
-
- private void updateAdbNotification(boolean adbEnabled) {
- if (mNotificationManager == null) return;
- if (adbEnabled) {
- if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
-
- if (!mAdbNotificationShown) {
- Resources r = mContext.getResources();
- CharSequence title = r.getText(
- com.android.internal.R.string.adb_active_notification_title);
- CharSequence message = r.getText(
- com.android.internal.R.string.adb_active_notification_message);
-
- if (mAdbNotification == null) {
- mAdbNotification = new Notification();
- mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb;
- mAdbNotification.when = 0;
- mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
- mAdbNotification.tickerText = title;
- mAdbNotification.defaults = 0; // please be quiet
- mAdbNotification.sound = null;
- mAdbNotification.vibrate = null;
- }
-
- Intent intent = new Intent(
- Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- // Note: we are hard-coding the component because this is
- // an important security UI that we don't want anyone
- // intercepting.
- intent.setComponent(new ComponentName("com.android.settings",
- "com.android.settings.DevelopmentSettings"));
- PendingIntent pi = PendingIntent.getActivity(mContext, 0,
- intent, 0);
-
- mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
-
- mAdbNotificationShown = true;
- mNotificationManager.notify(
- com.android.internal.R.string.adb_active_notification_title,
- mAdbNotification);
- }
- } else if (mAdbNotificationShown) {
- mAdbNotificationShown = false;
- mNotificationManager.cancel(
- com.android.internal.R.string.adb_active_notification_title);
- }
- }
-
/*
* Listens for uevent messages from the kernel to monitor the USB state
*/
private final UEventObserver mUEventObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "USB UEVENT: " + event.toString());
- }
+ if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
String state = event.get("USB_STATE");
String accessory = event.get("ACCESSORY");
if (state != null) {
mHandler.updateState(state);
} else if ("START".equals(accessory)) {
- Slog.d(TAG, "got accessory start");
+ if (DEBUG) Slog.d(TAG, "got accessory start");
setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false);
}
}
@@ -237,6 +134,7 @@ public class UsbDeviceManager {
mSettingsManager = settingsManager;
PackageManager pm = mContext.getPackageManager();
mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
+ initRndisAddress();
// create a thread for our Handler
HandlerThread thread = new HandlerThread("UsbDeviceManager",
@@ -271,6 +169,29 @@ public class UsbDeviceManager {
mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
}
+ private static void initRndisAddress() {
+ // configure RNDIS ethernet address based on our serial number using the same algorithm
+ // we had been previously using in kernel board files
+ final int ETH_ALEN = 6;
+ int address[] = new int[ETH_ALEN];
+ // first byte is 0x02 to signify a locally administered address
+ address[0] = 0x02;
+
+ String serial = SystemProperties.get("ro.serialno", "1234567890ABCDEF");
+ int serialLength = serial.length();
+ // XOR the USB serial across the remaining 5 bytes
+ for (int i = 0; i < serialLength; i++) {
+ address[i % (ETH_ALEN - 1) + 1] ^= (int)serial.charAt(i);
+ }
+ String addrString = String.format("%02X:%02X:%02X:%02X:%02X:%02X",
+ address[0], address[1], address[2], address[3], address[4], address[5]);
+ try {
+ FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
+ }
+ }
+
private static String addFunction(String functions, String function) {
if (!containsFunction(functions, function)) {
if (functions.length() > 0) {
@@ -319,16 +240,32 @@ public class UsbDeviceManager {
private String mDefaultFunctions;
private UsbAccessory mCurrentAccessory;
private boolean mDeferAccessoryAttached;
+ private int mUsbNotificationId;
+ private boolean mAdbNotificationShown;
+
+ private static final int NOTIFICATION_NONE = 0;
+ private static final int NOTIFICATION_MTP = 1;
+ private static final int NOTIFICATION_PTP = 2;
+ private static final int NOTIFICATION_INSTALLER = 3;
+ private static final int NOTIFICATION_ADB = 4;
public UsbHandler() {
- // Read initial USB state
try {
+ // sanity check the sys.usb.config system property
+ // this may be necessary if we crashed while switching USB configurations
+ String config = SystemProperties.get("sys.usb.config", "none");
+ if (config.equals("none")) {
+ String persistConfig = SystemProperties.get("persist.sys.usb.config", "none");
+ Slog.w(TAG, "resetting config to persistent property: " + persistConfig);
+ SystemProperties.set("sys.usb.config", persistConfig);
+ }
+
+ // Read initial USB state
mCurrentFunctions = FileUtils.readTextFile(
new File(FUNCTIONS_PATH), 0, null).trim();
mDefaultFunctions = mCurrentFunctions;
String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
updateState(state);
-
mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
// Upgrade step for previous versions that used persist.service.adb.enable
@@ -414,12 +351,12 @@ public class UsbDeviceManager {
} catch (InterruptedException e) {
}
}
- Log.e(TAG, "waitForState(" + state + ") FAILED");
+ Slog.e(TAG, "waitForState(" + state + ") FAILED");
return false;
}
private boolean setUsbConfig(String config) {
- Log.d(TAG, "setUsbConfig(" + config + ")");
+ if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
// set the new configuration
SystemProperties.set("sys.usb.config", config);
return waitForState(config);
@@ -428,7 +365,7 @@ public class UsbDeviceManager {
private void doSetCurrentFunctions(String functions) {
if (!mCurrentFunctions.equals(functions)) {
if (!setUsbConfig("none") || !setUsbConfig(functions)) {
- Log.e(TAG, "Failed to switch USB configuration to " + functions);
+ Slog.e(TAG, "Failed to switch USB configuration to " + functions);
// revert to previous configuration if we fail
setUsbConfig(mCurrentFunctions);
} else {
@@ -438,6 +375,7 @@ public class UsbDeviceManager {
}
private void setAdbEnabled(boolean enable) {
+ if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
if (enable != mAdbEnabled) {
mAdbEnabled = enable;
String functions;
@@ -449,7 +387,7 @@ public class UsbDeviceManager {
functions = removeFunction(mDefaultFunctions, UsbManager.USB_FUNCTION_ADB);
}
setCurrentFunction(functions, true);
- updateAdbNotification(mAdbEnabled && mConnected);
+ updateAdbNotification();
}
}
@@ -469,7 +407,7 @@ public class UsbDeviceManager {
String[] strings = nativeGetAccessoryStrings();
if (strings != null) {
mCurrentAccessory = new UsbAccessory(strings);
- Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+ Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
// defer accessoryAttached if system is not ready
if (mSystemReady) {
mSettingsManager.accessoryAttached(mCurrentAccessory);
@@ -477,12 +415,12 @@ public class UsbDeviceManager {
mDeferAccessoryAttached = true;
}
} else {
- Log.e(TAG, "nativeGetAccessoryStrings failed");
+ Slog.e(TAG, "nativeGetAccessoryStrings failed");
}
} else if (!mConnected) {
// make sure accessory mode is off
// and restore default functions
- Log.d(TAG, "exited USB accessory mode");
+ Slog.d(TAG, "exited USB accessory mode");
setEnabledFunctions(mDefaultFunctions);
if (mCurrentAccessory != null) {
@@ -517,8 +455,8 @@ public class UsbDeviceManager {
case MSG_UPDATE_STATE:
mConnected = (msg.arg1 == 1);
mConfigured = (msg.arg2 == 1);
- updateUsbNotification(mConnected);
- updateAdbNotification(mAdbEnabled && mConnected);
+ updateUsbNotification();
+ updateAdbNotification();
if (containsFunction(mCurrentFunctions,
UsbManager.USB_FUNCTION_ACCESSORY)) {
updateCurrentAccessory();
@@ -562,8 +500,8 @@ public class UsbDeviceManager {
}
break;
case MSG_SYSTEM_READY:
- updateUsbNotification(mConnected);
- updateAdbNotification(mAdbEnabled && mConnected);
+ updateUsbNotification();
+ updateAdbNotification();
updateUsbState();
if (mCurrentAccessory != null && mDeferAccessoryAttached) {
mSettingsManager.accessoryAttached(mCurrentAccessory);
@@ -576,6 +514,106 @@ public class UsbDeviceManager {
return mCurrentAccessory;
}
+ private void updateUsbNotification() {
+ if (mNotificationManager == null || !mUseUsbNotification) return;
+ if (mConnected) {
+ Resources r = mContext.getResources();
+ CharSequence title = null;
+ int id = NOTIFICATION_NONE;
+ if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) {
+ title = r.getText(
+ com.android.internal.R.string.usb_mtp_notification_title);
+ id = NOTIFICATION_MTP;
+ } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) {
+ title = r.getText(
+ com.android.internal.R.string.usb_ptp_notification_title);
+ id = NOTIFICATION_PTP;
+ } else if (containsFunction(mCurrentFunctions,
+ UsbManager.USB_FUNCTION_MASS_STORAGE)) {
+ title = r.getText(
+ com.android.internal.R.string.usb_cd_installer_notification_title);
+ id = NOTIFICATION_INSTALLER;
+ } else {
+ Slog.e(TAG, "No known USB function in updateUsbNotification");
+ }
+ if (id != mUsbNotificationId) {
+ // clear notification if title needs changing
+ if (mUsbNotificationId != NOTIFICATION_NONE) {
+ mNotificationManager.cancel(mUsbNotificationId);
+ mUsbNotificationId = NOTIFICATION_NONE;
+ }
+ }
+ if (mUsbNotificationId == NOTIFICATION_NONE) {
+ CharSequence message = r.getText(
+ com.android.internal.R.string.usb_notification_message);
+
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.tickerText = title;
+ notification.defaults = 0; // please be quiet
+ notification.sound = null;
+ notification.vibrate = null;
+
+ Intent intent = new Intent(
+ Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ intent.setComponent(new ComponentName("com.android.settings",
+ "com.android.settings.UsbSettings"));
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+ intent, 0);
+ notification.setLatestEventInfo(mContext, title, message, pi);
+ mNotificationManager.notify(id, notification);
+ mUsbNotificationId = id;
+ }
+
+ } else if (mUsbNotificationId != NOTIFICATION_NONE) {
+ mNotificationManager.cancel(mUsbNotificationId);
+ mUsbNotificationId = NOTIFICATION_NONE;
+ }
+ }
+
+ private void updateAdbNotification() {
+ if (mNotificationManager == null) return;
+ if (mAdbEnabled && mConnected) {
+ if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
+
+ if (!mAdbNotificationShown) {
+ Resources r = mContext.getResources();
+ CharSequence title = r.getText(
+ com.android.internal.R.string.adb_active_notification_title);
+ CharSequence message = r.getText(
+ com.android.internal.R.string.adb_active_notification_message);
+
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_sys_adb;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.tickerText = title;
+ notification.defaults = 0; // please be quiet
+ notification.sound = null;
+ notification.vibrate = null;
+
+ Intent intent = new Intent(
+ Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ intent.setComponent(new ComponentName("com.android.settings",
+ "com.android.settings.DevelopmentSettings"));
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+ intent, 0);
+ notification.setLatestEventInfo(mContext, title, message, pi);
+ mAdbNotificationShown = true;
+ mNotificationManager.notify(NOTIFICATION_ADB, notification);
+ }
+ } else if (mAdbNotificationShown) {
+ mAdbNotificationShown = false;
+ mNotificationManager.cancel(NOTIFICATION_ADB);
+ }
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw) {
pw.println(" USB Device State:");
pw.println(" Current Functions: " + mCurrentFunctions);
@@ -592,22 +630,23 @@ public class UsbDeviceManager {
}
/* opens the currently attached USB accessory */
- public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
- UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
- if (currentAccessory == null) {
- throw new IllegalArgumentException("no accessory attached");
- }
- if (!currentAccessory.equals(accessory)) {
- String error = accessory.toString()
- + " does not match current accessory "
- + currentAccessory;
- throw new IllegalArgumentException(error);
- }
- mSettingsManager.checkPermission(accessory);
- return nativeOpenAccessory();
+ public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+ UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
+ if (currentAccessory == null) {
+ throw new IllegalArgumentException("no accessory attached");
+ }
+ if (!currentAccessory.equals(accessory)) {
+ String error = accessory.toString()
+ + " does not match current accessory "
+ + currentAccessory;
+ throw new IllegalArgumentException(error);
}
+ mSettingsManager.checkPermission(accessory);
+ return nativeOpenAccessory();
+ }
public void setCurrentFunction(String function, boolean makeDefault) {
+ if (DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default: " + makeDefault);
mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION, function, makeDefault);
}
diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java
index 923b049..0a0ff59 100644
--- a/services/java/com/android/server/usb/UsbHostManager.java
+++ b/services/java/com/android/server/usb/UsbHostManager.java
@@ -37,7 +37,6 @@ import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.UEventObserver;
import android.provider.Settings;
-import android.util.Log;
import android.util.Slog;
import java.io.File;
@@ -112,7 +111,7 @@ public class UsbHostManager {
synchronized (mLock) {
if (mDevices.get(deviceName) != null) {
- Log.w(TAG, "device already on mDevices list: " + deviceName);
+ Slog.w(TAG, "device already on mDevices list: " + deviceName);
return;
}
@@ -148,7 +147,7 @@ public class UsbHostManager {
} catch (Exception e) {
// beware of index out of bound exceptions, which might happen if
// a device does not set bNumEndpoints correctly
- Log.e(TAG, "error parsing USB descriptors", e);
+ Slog.e(TAG, "error parsing USB descriptors", e);
return;
}
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 9113677..0baafbb 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -35,7 +35,7 @@ import android.hardware.usb.UsbManager;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Process;
-import android.util.Log;
+import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.Xml;
@@ -62,6 +62,7 @@ import java.util.List;
class UsbSettingsManager {
private static final String TAG = "UsbSettingsManager";
+ private static final boolean DEBUG = false;
private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
private final Context mContext;
@@ -410,9 +411,9 @@ class UsbSettingsManager {
}
}
} catch (FileNotFoundException e) {
- Log.w(TAG, "settings file not found");
+ if (DEBUG) Slog.d(TAG, "settings file not found");
} catch (Exception e) {
- Log.e(TAG, "error reading settings file, deleting to start fresh", e);
+ Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
sSettingsFile.delete();
} finally {
if (stream != null) {
@@ -428,7 +429,7 @@ class UsbSettingsManager {
FileOutputStream fos = null;
try {
FileOutputStream fstr = new FileOutputStream(sSettingsFile);
- Log.d(TAG, "writing settings to " + fstr);
+ if (DEBUG) Slog.d(TAG, "writing settings to " + fstr);
BufferedOutputStream str = new BufferedOutputStream(fstr);
FastXmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(str, "utf-8");
@@ -457,7 +458,7 @@ class UsbSettingsManager {
FileUtils.sync(fstr);
str.close();
} catch (Exception e) {
- Log.e(TAG, "error writing settings file, deleting to start fresh", e);
+ Slog.e(TAG, "error writing settings file, deleting to start fresh", e);
sSettingsFile.delete();
}
}
@@ -472,7 +473,7 @@ class UsbSettingsManager {
try {
parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
if (parser == null) {
- Log.w(TAG, "no meta-data for " + info);
+ Slog.w(TAG, "no meta-data for " + info);
return false;
}
@@ -494,7 +495,7 @@ class UsbSettingsManager {
XmlUtils.nextElement(parser);
}
} catch (Exception e) {
- Log.w(TAG, "Unable to load component info " + info.toString(), e);
+ Slog.w(TAG, "Unable to load component info " + info.toString(), e);
} finally {
if (parser != null) parser.close();
}
@@ -553,7 +554,7 @@ class UsbSettingsManager {
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- Log.d(TAG, "usbDeviceRemoved, sending " + intent);
+ if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
mContext.sendBroadcast(intent);
}
@@ -604,7 +605,7 @@ class UsbSettingsManager {
try {
mContext.startActivity(dialogIntent);
} catch (ActivityNotFoundException e) {
- Log.e(TAG, "unable to start UsbAccessoryUriActivity");
+ Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
}
}
}
@@ -652,7 +653,7 @@ class UsbSettingsManager {
defaultRI.activityInfo.name));
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
- Log.e(TAG, "startActivity failed", e);
+ Slog.e(TAG, "startActivity failed", e);
}
} else {
Intent resolverIntent = new Intent();
@@ -679,7 +680,7 @@ class UsbSettingsManager {
try {
mContext.startActivity(resolverIntent);
} catch (ActivityNotFoundException e) {
- Log.e(TAG, "unable to start activity " + resolverIntent);
+ Slog.e(TAG, "unable to start activity " + resolverIntent);
}
}
}
@@ -733,7 +734,7 @@ class UsbSettingsManager {
XmlUtils.nextElement(parser);
}
} catch (Exception e) {
- Log.w(TAG, "Unable to load component info " + aInfo.toString(), e);
+ Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
} finally {
if (parser != null) parser.close();
}
@@ -751,7 +752,7 @@ class UsbSettingsManager {
info = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
- Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
+ Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
return;
}
@@ -831,7 +832,7 @@ class UsbSettingsManager {
try {
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
- Log.e(TAG, "unable to start UsbPermissionActivity");
+ Slog.e(TAG, "unable to start UsbPermissionActivity");
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -847,7 +848,7 @@ class UsbSettingsManager {
try {
pi.send(mContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "requestPermission PendingIntent was cancelled");
+ if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
}
return;
}
@@ -867,7 +868,7 @@ class UsbSettingsManager {
try {
pi.send(mContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "requestPermission PendingIntent was cancelled");
+ if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
}
return;
}
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index ee69311..65007f9 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -551,18 +551,6 @@ public class InputManager {
android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
== PackageManager.PERMISSION_GRANTED;
}
-
- @SuppressWarnings("unused")
- public boolean filterTouchEvents() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_filterTouchEvents);
- }
-
- @SuppressWarnings("unused")
- public boolean filterJumpyTouchEvents() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_filterJumpyTouchEvents);
- }
@SuppressWarnings("unused")
public int getVirtualKeyQuietTimeMillis() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 3bf309b..be21ac0 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -231,7 +231,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Maximum number of milliseconds to wait for input devices to be enumerated before
// proceding with safe mode detection.
private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000;
-
+
// Default input dispatching timeout in nanoseconds.
static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
@@ -4886,8 +4886,13 @@ public class WindowManagerService extends IWindowManager.Stub
int fw = frame.width();
int fh = frame.height();
- // First try reducing to fit in x dimension.
- scale = width/(float)fw;
+ // Constrain thumbnail to smaller of screen width or height. Assumes aspect
+ // of thumbnail is the same as the screen (in landscape) or square.
+ if (dw <= dh) {
+ scale = width / (float) fw; // portrait
+ } else {
+ scale = height / (float) fh; // landscape
+ }
// The screen shot will contain the entire screen.
dw = (int)(dw*scale);
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 8a46ab0..14a4109 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -69,8 +69,6 @@ static struct {
jmethodID interceptKeyBeforeDispatching;
jmethodID dispatchUnhandledKey;
jmethodID checkInjectEventsPermission;
- jmethodID filterTouchEvents;
- jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames;
jmethodID getKeyRepeatTimeout;
@@ -381,18 +379,6 @@ bool NativeInputManager::getDisplayInfo(int32_t displayId,
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
JNIEnv* env = jniEnv();
- jboolean filterTouchEvents = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.filterTouchEvents);
- if (!checkAndClearExceptionFromCallback(env, "filterTouchEvents")) {
- outConfig->filterTouchEvents = filterTouchEvents;
- }
-
- jboolean filterJumpyTouchEvents = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.filterJumpyTouchEvents);
- if (!checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) {
- outConfig->filterJumpyTouchEvents = filterJumpyTouchEvents;
- }
-
jint virtualKeyQuietTime = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.getVirtualKeyQuietTimeMillis);
if (!checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) {
@@ -1405,12 +1391,6 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, clazz,
"checkInjectEventsPermission", "(II)Z");
- GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, clazz,
- "filterTouchEvents", "()Z");
-
- GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, clazz,
- "filterJumpyTouchEvents", "()Z");
-
GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyQuietTimeMillis, clazz,
"getVirtualKeyQuietTimeMillis", "()I");
diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp
index ae7fbfe..5f920f1 100644
--- a/services/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/jni/com_android_server_connectivity_Vpn.cpp
@@ -42,6 +42,9 @@
namespace android
{
+static int inet4 = -1;
+static int inet6 = -1;
+
static inline in_addr_t *as_in_addr(sockaddr *sa) {
return &((sockaddr_in *)sa)->sin_addr.s_addr;
}
@@ -51,11 +54,9 @@ static inline in_addr_t *as_in_addr(sockaddr *sa) {
#define SYSTEM_ERROR -1
#define BAD_ARGUMENT -2
-static int create_interface(int mtu, char *name, int *index)
+static int create_interface(char *name, int *index, int mtu)
{
- int tun = open("/dev/tun", O_RDWR);
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
- int flags;
+ int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
ifreq ifr4;
memset(&ifr4, 0, sizeof(ifr4));
@@ -87,32 +88,20 @@ static int create_interface(int mtu, char *name, int *index)
goto error;
}
- // Make it non-blocking.
- flags = fcntl(tun, F_GETFL, 0);
- if (flags == -1 || fcntl(tun, F_SETFL, flags | O_NONBLOCK)) {
- LOGE("Cannot set non-blocking on %s: %s", ifr4.ifr_name, strerror(errno));
- goto error;
- }
-
- strcpy(name, ifr4.ifr_name);
+ strncpy(name, ifr4.ifr_name, IFNAMSIZ);
*index = ifr4.ifr_ifindex;
- close(inet4);
return tun;
error:
close(tun);
- close(inet4);
return SYSTEM_ERROR;
}
static int set_addresses(const char *name, int index, const char *addresses)
{
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
- int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
-
ifreq ifr4;
memset(&ifr4, 0, sizeof(ifr4));
- strcpy(ifr4.ifr_name, name);
+ strncpy(ifr4.ifr_name, name, IFNAMSIZ);
ifr4.ifr_addr.sa_family = AF_INET;
in6_ifreq ifr6;
@@ -121,7 +110,6 @@ static int set_addresses(const char *name, int index, const char *addresses)
char address[65];
int prefix;
-
int chars;
int count = 0;
@@ -164,7 +152,7 @@ static int set_addresses(const char *name, int index, const char *addresses)
break;
}
}
- LOGV("Address added on %s: %s/%d", name, address, prefix);
+ LOGD("Address added on %s: %s/%d", name, address, prefix);
++count;
}
@@ -177,16 +165,11 @@ static int set_addresses(const char *name, int index, const char *addresses)
count = BAD_ARGUMENT;
}
- close(inet4);
- close(inet6);
return count;
}
static int set_routes(const char *name, int index, const char *routes)
{
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
- int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
-
rtentry rt4;
memset(&rt4, 0, sizeof(rt4));
rt4.rt_dev = (char *)name;
@@ -201,7 +184,6 @@ static int set_routes(const char *name, int index, const char *routes)
char address[65];
int prefix;
-
int chars;
int count = 0;
@@ -211,32 +193,50 @@ static int set_routes(const char *name, int index, const char *routes)
if (strchr(address, ':')) {
// Add an IPv6 route.
if (inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 ||
- prefix < 1 || prefix > 128) {
+ prefix < 0 || prefix > 128) {
count = BAD_ARGUMENT;
break;
}
- rt6.rtmsg_dst_len = prefix;
+ rt6.rtmsg_dst_len = prefix ? prefix : 1;
if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
break;
}
+
+ if (!prefix) {
+ // Split the route instead of replacing the default route.
+ rt6.rtmsg_dst.s6_addr[0] ^= 0x80;
+ if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
+ count = SYSTEM_ERROR;
+ break;
+ }
+ }
} else {
// Add an IPv4 route.
if (inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 ||
- prefix < 1 || prefix > 32) {
+ prefix < 0 || prefix > 32) {
count = BAD_ARGUMENT;
break;
}
- in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
+ in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0x80000000;
*as_in_addr(&rt4.rt_genmask) = htonl(mask);
if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
break;
}
+
+ if (!prefix) {
+ // Split the route instead of replacing the default route.
+ *as_in_addr(&rt4.rt_dst) ^= htonl(0x80000000);
+ if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
+ count = SYSTEM_ERROR;
+ break;
+ }
+ }
}
- LOGV("Route added on %s: %s/%d", name, address, prefix);
+ LOGD("Route added on %s: %s/%d", name, address, prefix);
++count;
}
@@ -250,8 +250,6 @@ static int set_routes(const char *name, int index, const char *routes)
count = BAD_ARGUMENT;
}
- close(inet4);
- close(inet6);
return count;
}
@@ -262,31 +260,25 @@ static int get_interface_name(char *name, int tun)
LOGE("Cannot get interface name: %s", strerror(errno));
return SYSTEM_ERROR;
}
- strcpy(name, ifr4.ifr_name);
+ strncpy(name, ifr4.ifr_name, IFNAMSIZ);
return 0;
}
static int reset_interface(const char *name)
{
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-
ifreq ifr4;
- ifr4.ifr_flags = 0;
strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+ ifr4.ifr_flags = 0;
if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
LOGE("Cannot reset %s: %s", name, strerror(errno));
- close(inet4);
return SYSTEM_ERROR;
}
- close(inet4);
return 0;
}
static int check_interface(const char *name)
{
- int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-
ifreq ifr4;
strncpy(ifr4.ifr_name, name, IFNAMSIZ);
ifr4.ifr_flags = 0;
@@ -294,13 +286,12 @@ static int check_interface(const char *name)
if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
LOGE("Cannot check %s: %s", name, strerror(errno));
}
- close(inet4);
return ifr4.ifr_flags;
}
-static int bind_to_interface(int fd, const char *name)
+static int bind_to_interface(int socket, const char *name)
{
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
+ if (setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
LOGE("Cannot bind socket to %s: %s", name, strerror(errno));
return SYSTEM_ERROR;
}
@@ -318,23 +309,22 @@ static void throwException(JNIEnv *env, int error, const char *message)
}
}
-static jint establish(JNIEnv *env, jobject thiz,
+static jint configure(JNIEnv *env, jobject thiz,
jint mtu, jstring jAddresses, jstring jRoutes)
{
char name[IFNAMSIZ];
int index;
- int tun = create_interface(mtu, name, &index);
+ int tun = create_interface(name, &index, mtu);
if (tun < 0) {
throwException(env, tun, "Cannot create interface");
return -1;
}
- LOGD("%s is created", name);
- const char *addresses;
- const char *routes;
+ const char *addresses = NULL;
+ const char *routes = NULL;
int count;
- // Addresses are required.
+ // At least one address must be set.
addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
if (!addresses) {
jniThrowNullPointerException(env, "address");
@@ -348,7 +338,7 @@ static jint establish(JNIEnv *env, jobject thiz,
}
LOGD("Configured %d address(es) on %s", count, name);
- // Routes are optional.
+ // On the contrary, routes are optional.
routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
if (routes) {
count = set_routes(name, index, routes);
@@ -368,10 +358,10 @@ error:
return -1;
}
-static jstring getName(JNIEnv *env, jobject thiz, jint fd)
+static jstring getName(JNIEnv *env, jobject thiz, jint tun)
{
char name[IFNAMSIZ];
- if (get_interface_name(name, fd) < 0) {
+ if (get_interface_name(name, tun) < 0) {
throwException(env, SYSTEM_ERROR, "Cannot get interface name");
return NULL;
}
@@ -380,24 +370,20 @@ static jstring getName(JNIEnv *env, jobject thiz, jint fd)
static void reset(JNIEnv *env, jobject thiz, jstring jName)
{
- const char *name = jName ?
- env->GetStringUTFChars(jName, NULL) : NULL;
+ const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
jniThrowNullPointerException(env, "name");
return;
}
if (reset_interface(name) < 0) {
throwException(env, SYSTEM_ERROR, "Cannot reset interface");
- } else {
- LOGD("%s is deactivated", name);
}
env->ReleaseStringUTFChars(jName, name);
}
static jint check(JNIEnv *env, jobject thiz, jstring jName)
{
- const char *name = jName ?
- env->GetStringUTFChars(jName, NULL) : NULL;
+ const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
jniThrowNullPointerException(env, "name");
return 0;
@@ -407,15 +393,14 @@ static jint check(JNIEnv *env, jobject thiz, jstring jName)
return flags;
}
-static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName)
+static void protect(JNIEnv *env, jobject thiz, jint socket, jstring jName)
{
- const char *name = jName ?
- env->GetStringUTFChars(jName, NULL) : NULL;
+ const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
jniThrowNullPointerException(env, "name");
return;
}
- if (bind_to_interface(fd, name) < 0) {
+ if (bind_to_interface(socket, name) < 0) {
throwException(env, SYSTEM_ERROR, "Cannot protect socket");
}
env->ReleaseStringUTFChars(jName, name);
@@ -424,15 +409,21 @@ static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName)
//------------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- {"nativeEstablish", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)establish},
- {"nativeGetName", "(I)Ljava/lang/String;", (void *)getName},
- {"nativeReset", "(Ljava/lang/String;)V", (void *)reset},
- {"nativeCheck", "(Ljava/lang/String;)I", (void *)check},
- {"nativeProtect", "(ILjava/lang/String;)V", (void *)protect},
+ {"jniConfigure", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)configure},
+ {"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
+ {"jniReset", "(Ljava/lang/String;)V", (void *)reset},
+ {"jniCheck", "(Ljava/lang/String;)I", (void *)check},
+ {"jniProtect", "(ILjava/lang/String;)V", (void *)protect},
};
int register_android_server_connectivity_Vpn(JNIEnv *env)
{
+ if (inet4 == -1) {
+ inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+ }
+ if (inet6 == -1) {
+ inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ }
return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
gMethods, NELEM(gMethods));
}
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index 6d4ad9a..87ffcba 100755
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -42,6 +42,7 @@ static jmethodID method_xtraDownloadRequest;
static jmethodID method_reportNiNotification;
static jmethodID method_requestRefLocation;
static jmethodID method_requestSetID;
+static jmethodID method_requestUtcTime;
static const GpsInterface* sGpsInterface = NULL;
static const GpsXtraInterface* sGpsXtraInterface = NULL;
@@ -122,6 +123,13 @@ static void release_wakelock_callback()
release_wake_lock(WAKE_LOCK_NAME);
}
+static void request_utc_time_callback()
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)
{
return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
@@ -137,6 +145,7 @@ GpsCallbacks sGpsCallbacks = {
acquire_wakelock_callback,
release_wakelock_callback,
create_thread_callback,
+ request_utc_time_callback,
};
static void xtra_download_request_callback()
@@ -232,6 +241,7 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env,
"(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");
method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");
+ method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V");
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 0ae7929..64d214b 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -31,6 +31,7 @@
#include <binder/BinderService.h>
#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
#include <gui/ISensorServer.h>
#include <gui/ISensorEventConnection.h>
@@ -58,8 +59,7 @@ namespace android {
*/
SensorService::SensorService()
- : mDump("android.permission.DUMP"),
- mInitCheck(NO_INIT)
+ : mInitCheck(NO_INIT)
{
}
@@ -166,12 +166,14 @@ SensorService::~SensorService()
delete mSensorMap.valueAt(i);
}
+static const String16 sDump("android.permission.DUMP");
+
status_t SensorService::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 1024;
char buffer[SIZE];
String8 result;
- if (!mDump.checkCalling()) {
+ if (!PermissionCache::checkCallingPermission(sDump)) {
snprintf(buffer, SIZE, "Permission Denial: "
"can't dump SurfaceFlinger from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 4d0f1d9..85f4ecb 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -27,7 +27,6 @@
#include <utils/RefBase.h>
#include <binder/BinderService.h>
-#include <binder/Permission.h>
#include <gui/Sensor.h>
#include <gui/SensorChannel.h>
@@ -117,7 +116,6 @@ class SensorService :
Vector<Sensor> mUserSensorList;
DefaultKeyedVector<int, SensorInterface*> mSensorMap;
Vector<SensorInterface *> mVirtualSensorList;
- Permission mDump;
status_t mInitCheck;
// protected by mLock
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index a774841..7bf3e0a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -99,10 +99,43 @@ uint32_t DisplayHardware::getMaxViewportDims() const {
mMaxViewportDims[0] : mMaxViewportDims[1];
}
+static status_t selectConfigForPixelFormat(
+ EGLDisplay dpy,
+ EGLint const* attrs,
+ PixelFormat format,
+ EGLConfig* outConfig)
+{
+ EGLConfig config = NULL;
+ EGLint numConfigs = -1, n=0;
+ eglGetConfigs(dpy, NULL, 0, &numConfigs);
+ EGLConfig* const configs = new EGLConfig[numConfigs];
+ eglChooseConfig(dpy, attrs, configs, numConfigs, &n);
+ for (int i=0 ; i<n ; i++) {
+ EGLint nativeVisualId = 0;
+ eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &nativeVisualId);
+ if (nativeVisualId>0 && format == nativeVisualId) {
+ *outConfig = configs[i];
+ delete [] configs;
+ return NO_ERROR;
+ }
+ }
+ delete [] configs;
+ return NAME_NOT_FOUND;
+}
+
+
void DisplayHardware::init(uint32_t dpy)
{
mNativeWindow = new FramebufferNativeWindow();
framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
+ if (!fbDev) {
+ LOGE("Display subsystem failed to initialize. check logs. exiting...");
+ exit(0);
+ }
+
+ int format;
+ ANativeWindow const * const window = mNativeWindow.get();
+ window->query(window, NATIVE_WINDOW_FORMAT, &format);
mDpiX = mNativeWindow->xdpi;
mDpiY = mNativeWindow->ydpi;
mRefreshRate = fbDev->fps;
@@ -111,11 +144,13 @@ void DisplayHardware::init(uint32_t dpy)
EGLint numConfigs=0;
EGLSurface surface;
EGLContext context;
+ EGLBoolean result;
+ status_t err;
// initialize EGL
EGLint attribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_NONE, 0,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_NONE, 0,
EGL_NONE
};
@@ -136,9 +171,8 @@ void DisplayHardware::init(uint32_t dpy)
eglInitialize(display, NULL, NULL);
eglGetConfigs(display, NULL, 0, &numConfigs);
- EGLConfig config;
- status_t err = EGLUtils::selectConfigForNativeWindow(
- display, attribs, mNativeWindow.get(), &config);
+ EGLConfig config = NULL;
+ err = selectConfigForPixelFormat(display, attribs, format, &config);
LOGE_IF(err, "couldn't find an EGLConfig matching the screen format");
EGLint r,g,b,a;
@@ -219,7 +253,11 @@ void DisplayHardware::init(uint32_t dpy)
* Gather OpenGL ES extensions
*/
- eglMakeCurrent(display, surface, surface, context);
+ result = eglMakeCurrent(display, surface, surface, context);
+ if (!result) {
+ LOGE("Couldn't create a working GLES context. check logs. exiting...");
+ exit(0);
+ }
GLExtensions& extensions(GLExtensions::getInstance());
extensions.initWithGLStrings(
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2bab6a8..35e29a6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,9 +101,8 @@ Layer::~Layer()
}
void Layer::onFrameQueued() {
- if (android_atomic_or(1, &mQueuedFrames) == 0) {
- mFlinger->signalEvent();
- }
+ android_atomic_inc(&mQueuedFrames);
+ mFlinger->signalEvent();
}
// called with SurfaceFlinger::mStateLock as soon as the layer is entered
@@ -406,20 +405,18 @@ bool Layer::isCropped() const {
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
- if (android_atomic_and(0, &mQueuedFrames)) {
+ if (mQueuedFrames > 0) {
+ // signal another event if we have more frames pending
+ if (android_atomic_dec(&mQueuedFrames) > 1) {
+ mFlinger->signalEvent();
+ }
+
if (mSurfaceTexture->updateTexImage() < NO_ERROR) {
// something happened!
recomputeVisibleRegions = true;
return;
}
- // signal another event if we have more frames waiting
- if (mSurfaceTexture->getQueuedCount()) {
- if (android_atomic_or(1, &mQueuedFrames) == 0) {
- mFlinger->signalEvent();
- }
- }
-
mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
mSurfaceTexture->getTransformMatrix(mTextureMatrix);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index bcd8c83..c86c659 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -228,13 +228,18 @@ void LayerBase::validateVisibility(const Transform& planeTransform)
const Layer::State& s(drawingState());
const Transform tr(planeTransform * s.transform);
const bool transformed = tr.transformed();
-
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const uint32_t hw_h = hw.getHeight();
+
uint32_t w = s.w;
uint32_t h = s.h;
tr.transform(mVertices[0], 0, 0);
tr.transform(mVertices[1], 0, h);
tr.transform(mVertices[2], w, h);
tr.transform(mVertices[3], w, 0);
+ for (size_t i=0 ; i<4 ; i++)
+ mVertices[i][1] = hw_h - mVertices[i][1];
+
if (UNLIKELY(transformed)) {
// NOTE: here we could also punt if we have too many rectangles
// in the transparent region
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 97edfee..b0881a4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -32,6 +32,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryHeapBase.h>
+#include <binder/PermissionCache.h>
#include <utils/String8.h>
#include <utils/String16.h>
@@ -67,17 +68,19 @@
namespace android {
// ---------------------------------------------------------------------------
+const String16 sHardwareTest("android.permission.HARDWARE_TEST");
+const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
+const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
+const String16 sDump("android.permission.DUMP");
+
+// ---------------------------------------------------------------------------
+
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(), Thread(false),
mTransactionFlags(0),
- mTransactionCount(0),
mResizeTransationPending(false),
mLayersRemoved(false),
mBootTime(systemTime()),
- mHardwareTest("android.permission.HARDWARE_TEST"),
- mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
- mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
- mDump("android.permission.DUMP"),
mVisibleRegionsDirty(false),
mHwWorkListDirty(false),
mDeferReleaseConsole(false),
@@ -160,9 +163,34 @@ void SurfaceFlinger::bootFinished()
const nsecs_t duration = now - mBootTime;
LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mBootFinished = true;
+
+ // wait patiently for the window manager death
+ const String16 name("window");
+ sp<IBinder> window(defaultServiceManager()->getService(name));
+ if (window != 0) {
+ window->linkToDeath(this);
+ }
+
+ // stop boot animation
property_set("ctl.stop", "bootanim");
}
+void SurfaceFlinger::binderDied(const wp<IBinder>& who)
+{
+ // the window manager died on us. prepare its eulogy.
+
+ // unfreeze the screen in case it was... frozen
+ mFreezeDisplayTime = 0;
+ mFreezeCount = 0;
+ mFreezeDisplay = false;
+
+ // reset screen orientation
+ setOrientation(0, eOrientationDefault, 0);
+
+ // restart the boot-animation
+ property_set("ctl.start", "bootanim");
+}
+
void SurfaceFlinger::onFirstRef()
{
run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
@@ -248,7 +276,8 @@ status_t SurfaceFlinger::readyToRun()
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
- glOrthof(0, w, h, 0, 0, 1);
+ // put the origin in the left-bottom corner
+ glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h
mReadyToRunBarrier.open();
@@ -381,13 +410,11 @@ bool SurfaceFlinger::threadLoop()
handleConsoleEvents();
}
- if (LIKELY(mTransactionCount == 0)) {
- // if we're in a global transaction, don't do anything.
- const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
- uint32_t transactionFlags = peekTransactionFlags(mask);
- if (LIKELY(transactionFlags)) {
- handleTransaction(transactionFlags);
- }
+ // if we're in a global transaction, don't do anything.
+ const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
+ uint32_t transactionFlags = peekTransactionFlags(mask);
+ if (UNLIKELY(transactionFlags)) {
+ handleTransaction(transactionFlags);
}
// post surfaces (if needed)
@@ -1172,28 +1199,33 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags)
return old;
}
-void SurfaceFlinger::openGlobalTransaction()
-{
- android_atomic_inc(&mTransactionCount);
-}
-void SurfaceFlinger::closeGlobalTransaction()
-{
- if (android_atomic_dec(&mTransactionCount) == 1) {
- signalEvent();
+void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state) {
+ Mutex::Autolock _l(mStateLock);
- // if there is a transaction with a resize, wait for it to
- // take effect before returning.
- Mutex::Autolock _l(mStateLock);
- while (mResizeTransationPending) {
- status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
- if (CC_UNLIKELY(err != NO_ERROR)) {
- // just in case something goes wrong in SF, return to the
- // called after a few seconds.
- LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!");
- mResizeTransationPending = false;
- break;
- }
+ uint32_t flags = 0;
+ const size_t count = state.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const ComposerState& s(state[i]);
+ sp<Client> client( static_cast<Client *>(s.client.get()) );
+ flags |= setClientStateLocked(client, s.state);
+ }
+ if (flags) {
+ setTransactionFlags(flags);
+ }
+
+ signalEvent();
+
+ // if there is a transaction with a resize, wait for it to
+ // take effect before returning.
+ while (mResizeTransationPending) {
+ status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
+ if (CC_UNLIKELY(err != NO_ERROR)) {
+ // just in case something goes wrong in SF, return to the
+ // called after a few seconds.
+ LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!");
+ mResizeTransationPending = false;
+ break;
}
}
}
@@ -1389,60 +1421,52 @@ status_t SurfaceFlinger::destroySurface(const wp<LayerBaseClient>& layer)
return err;
}
-status_t SurfaceFlinger::setClientState(
+uint32_t SurfaceFlinger::setClientStateLocked(
const sp<Client>& client,
- int32_t count,
- const layer_state_t* states)
+ const layer_state_t& s)
{
- Mutex::Autolock _l(mStateLock);
uint32_t flags = 0;
- for (int i=0 ; i<count ; i++) {
- const layer_state_t& s(states[i]);
- sp<LayerBaseClient> layer(client->getLayerUser(s.surface));
- if (layer != 0) {
- const uint32_t what = s.what;
- if (what & ePositionChanged) {
- if (layer->setPosition(s.x, s.y))
- flags |= eTraversalNeeded;
- }
- if (what & eLayerChanged) {
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setLayer(s.z)) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- if (what & eSizeChanged) {
- if (layer->setSize(s.w, s.h)) {
- flags |= eTraversalNeeded;
- mResizeTransationPending = true;
- }
- }
- if (what & eAlphaChanged) {
- if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
- flags |= eTraversalNeeded;
- }
- if (what & eMatrixChanged) {
- if (layer->setMatrix(s.matrix))
- flags |= eTraversalNeeded;
- }
- if (what & eTransparentRegionChanged) {
- if (layer->setTransparentRegionHint(s.transparentRegion))
- flags |= eTraversalNeeded;
+ sp<LayerBaseClient> layer(client->getLayerUser(s.surface));
+ if (layer != 0) {
+ const uint32_t what = s.what;
+ if (what & ePositionChanged) {
+ if (layer->setPosition(s.x, s.y))
+ flags |= eTraversalNeeded;
+ }
+ if (what & eLayerChanged) {
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
+ if (layer->setLayer(s.z)) {
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(layer);
+ // we need traversal (state changed)
+ // AND transaction (list changed)
+ flags |= eTransactionNeeded|eTraversalNeeded;
}
- if (what & eVisibilityChanged) {
- if (layer->setFlags(s.flags, s.mask))
- flags |= eTraversalNeeded;
+ }
+ if (what & eSizeChanged) {
+ if (layer->setSize(s.w, s.h)) {
+ flags |= eTraversalNeeded;
+ mResizeTransationPending = true;
}
}
+ if (what & eAlphaChanged) {
+ if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
+ flags |= eTraversalNeeded;
+ }
+ if (what & eMatrixChanged) {
+ if (layer->setMatrix(s.matrix))
+ flags |= eTraversalNeeded;
+ }
+ if (what & eTransparentRegionChanged) {
+ if (layer->setTransparentRegionHint(s.transparentRegion))
+ flags |= eTraversalNeeded;
+ }
+ if (what & eVisibilityChanged) {
+ if (layer->setFlags(s.flags, s.mask))
+ flags |= eTraversalNeeded;
+ }
}
- if (flags) {
- setTransactionFlags(flags);
- }
- return NO_ERROR;
+ return flags;
}
void SurfaceFlinger::screenReleased(int dpy)
@@ -1464,7 +1488,8 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
const size_t SIZE = 4096;
char buffer[SIZE];
String8 result;
- if (!mDump.checkCalling()) {
+
+ if (!PermissionCache::checkCallingPermission(sDump)) {
snprintf(buffer, SIZE, "Permission Denial: "
"can't dump SurfaceFlinger from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
@@ -1583,8 +1608,7 @@ status_t SurfaceFlinger::onTransact(
{
switch (code) {
case CREATE_CONNECTION:
- case OPEN_GLOBAL_TRANSACTION:
- case CLOSE_GLOBAL_TRANSACTION:
+ case SET_TRANSACTION_STATE:
case SET_ORIENTATION:
case FREEZE_DISPLAY:
case UNFREEZE_DISPLAY:
@@ -1596,7 +1620,8 @@ status_t SurfaceFlinger::onTransact(
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) {
+ if ((uid != AID_GRAPHICS) &&
+ !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
LOGE("Permission Denial: "
"can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
@@ -1609,7 +1634,8 @@ status_t SurfaceFlinger::onTransact(
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
+ if ((uid != AID_GRAPHICS) &&
+ !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
LOGE("Permission Denial: "
"can't read framebuffer pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
@@ -1621,7 +1647,7 @@ status_t SurfaceFlinger::onTransact(
status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- if (UNLIKELY(!mHardwareTest.checkCalling())) {
+ if (UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
@@ -1766,7 +1792,7 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked()
}
GLfloat vtx[8];
- const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} };
+ const GLfloat texCoords[4][2] = { {0,1}, {0,1-v}, {u,1-v}, {u,1} };
glBindTexture(GL_TEXTURE_2D, tname);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -1775,6 +1801,22 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked()
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vtx);
+ /*
+ * Texture coordinate mapping
+ *
+ * u
+ * 1 +----------+---+
+ * | | | | image is inverted
+ * | V | | w.r.t. the texture
+ * 1-v +----------+ | coordinates
+ * | |
+ * | |
+ * | |
+ * 0 +--------------+
+ * 0 1
+ *
+ */
+
class s_curve_interpolator {
const float nbFrames, s, v;
public:
@@ -1825,7 +1867,12 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked()
};
// the full animation is 24 frames
- const int nbFrames = 12;
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.electron_frames", value, "24");
+ int nbFrames = (atoi(value) + 1) >> 1;
+ if (nbFrames <= 0) // just in case
+ nbFrames = 24;
+
s_curve_interpolator itr(nbFrames, 7.5f);
s_curve_interpolator itg(nbFrames, 8.0f);
s_curve_interpolator itb(nbFrames, 8.5f);
@@ -2200,10 +2247,11 @@ status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
// invert everything, b/c glReadPixel() below will invert the FB
glViewport(0, 0, sw, sh);
glScissor(0, 0, sw, sh);
+ glEnable(GL_SCISSOR_TEST);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
- glOrthof(0, hw_w, 0, hw_h, 0, 1);
+ glOrthof(0, hw_w, hw_h, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
// redraw the screen entirely...
@@ -2219,6 +2267,7 @@ status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
}
// XXX: this is needed on tegra
+ glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, sw, sh);
// check for errors and return screen capture
@@ -2404,8 +2453,7 @@ status_t Client::onTransact(
const int self_pid = getpid();
if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) {
// we're called from a different process, do the real check
- if (!checkCallingPermission(
- String16("android.permission.ACCESS_SURFACE_FLINGER")))
+ if (!PermissionCache::checkCallingPermission(sAccessSurfaceFlinger))
{
LOGE("Permission Denial: "
"can't openGlobalTransaction pid=%d, uid=%d", pid, uid);
@@ -2463,9 +2511,6 @@ sp<ISurface> Client::createSurface(
status_t Client::destroySurface(SurfaceID sid) {
return mFlinger->removeSurface(this, sid);
}
-status_t Client::setState(int32_t count, const layer_state_t* states) {
- return mFlinger->setClientState(this, count, states);
-}
// ---------------------------------------------------------------------------
@@ -2474,11 +2519,14 @@ GraphicBufferAlloc::GraphicBufferAlloc() {}
GraphicBufferAlloc::~GraphicBufferAlloc() {}
sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage) {
+ PixelFormat format, uint32_t usage, status_t* error) {
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(w, h, format, usage));
status_t err = graphicBuffer->initCheck();
+ *error = err;
if (err != 0 || graphicBuffer->handle == 0) {
- GraphicBuffer::dumpAllocationsToSystemLog();
+ if (err == NO_MEMORY) {
+ GraphicBuffer::dumpAllocationsToSystemLog();
+ }
LOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
"failed (%s), handle=%p",
w, h, strerror(-err), graphicBuffer->handle);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index af1ef04..15661f0 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -20,21 +20,20 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/SortedVector.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
#include <utils/Atomic.h>
#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
-#include <binder/IMemory.h>
-#include <binder/Permission.h>
#include <binder/BinderService.h>
+#include <binder/IMemory.h>
#include <ui/PixelFormat.h>
+#include <surfaceflinger/IGraphicBufferAlloc.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/ISurfaceComposerClient.h>
-#include <surfaceflinger/IGraphicBufferAlloc.h>
#include "Barrier.h"
#include "Layer.h"
@@ -71,14 +70,12 @@ public:
sp<LayerBaseClient> getLayerUser(int32_t i) const;
private:
-
// ISurfaceComposerClient interface
virtual sp<ISurface> createSurface(
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);
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
@@ -99,7 +96,7 @@ public:
GraphicBufferAlloc();
virtual ~GraphicBufferAlloc();
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage);
+ PixelFormat format, uint32_t usage, status_t* error);
};
// ---------------------------------------------------------------------------
@@ -150,6 +147,7 @@ enum {
class SurfaceFlinger :
public BinderService<SurfaceFlinger>,
public BnSurfaceComposer,
+ public IBinder::DeathRecipient,
protected Thread
{
public:
@@ -169,8 +167,7 @@ public:
virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc();
virtual sp<IMemoryHeap> getCblk() const;
virtual void bootFinished();
- virtual void openGlobalTransaction();
- virtual void closeGlobalTransaction();
+ virtual void setTransactionState(const Vector<ComposerState>& state);
virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags);
virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags);
virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
@@ -197,6 +194,10 @@ public:
sp<Layer> getLayer(const sp<ISurface>& sur) const;
private:
+ // DeathRecipient interface
+ virtual void binderDied(const wp<IBinder>& who);
+
+private:
friend class Client;
friend class LayerBase;
friend class LayerBaseClient;
@@ -221,8 +222,7 @@ private:
status_t removeSurface(const sp<Client>& client, SurfaceID sid);
status_t destroySurface(const wp<LayerBaseClient>& layer);
- status_t setClientState(const sp<Client>& client,
- int32_t count, const layer_state_t* states);
+ uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s);
class LayerVector : public SortedVector< sp<LayerBase> > {
public:
@@ -338,7 +338,6 @@ private:
mutable Mutex mStateLock;
State mCurrentState;
volatile int32_t mTransactionFlags;
- volatile int32_t mTransactionCount;
Condition mTransactionCV;
SortedVector< sp<LayerBase> > mLayerPurgatory;
bool mResizeTransationPending;
@@ -353,11 +352,7 @@ private:
surface_flinger_cblk_t* mServerCblk;
GLuint mWormholeTexName;
nsecs_t mBootTime;
- Permission mHardwareTest;
- Permission mAccessSurfaceFlinger;
- Permission mReadFramebuffer;
- Permission mDump;
-
+
// Can only accessed from the main thread, these members
// don't need synchronization
State mDrawingState;
diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
index 18c54b3..56b2a8f 100644
--- a/services/surfaceflinger/tests/resize/resize.cpp
+++ b/services/surfaceflinger/tests/resize/resize.cpp
@@ -43,9 +43,9 @@ int main(int argc, char** argv)
PIXEL_FORMAT_RGB_565);
- client->openTransaction();
+ SurfaceComposerClient::openGlobalTransaction();
surface->setLayer(100000);
- client->closeTransaction();
+ SurfaceComposerClient::closeGlobalTransaction();
Surface::SurfaceInfo info;
surface->lock(&info);
@@ -57,9 +57,9 @@ int main(int argc, char** argv)
android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h);
surface->unlockAndPost();
- client->openTransaction();
+ SurfaceComposerClient::openGlobalTransaction();
surface->setSize(320, 240);
- client->closeTransaction();
+ SurfaceComposerClient::closeGlobalTransaction();
IPCThreadState::self()->joinThreadPool();
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
index 5265f91..8e1c3fe 100644
--- a/services/surfaceflinger/tests/surface/surface.cpp
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -39,9 +39,9 @@ int main(int argc, char** argv)
sp<SurfaceControl> surfaceControl = client->createSurface(
getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565);
- client->openTransaction();
+ SurfaceComposerClient::openGlobalTransaction();
surfaceControl->setLayer(100000);
- client->closeTransaction();
+ SurfaceComposerClient::closeGlobalTransaction();
// pretend it went cross-process
Parcel parcel;
diff --git a/services/tests/servicestests/res/raw/xt_qtaguid_extended b/services/tests/servicestests/res/raw/xt_qtaguid_extended
new file mode 100644
index 0000000..5bef3dd
--- /dev/null
+++ b/services/tests/servicestests/res/raw/xt_qtaguid_extended
@@ -0,0 +1,3 @@
+acct_tag_hex uid_tag_int iface rx_bytes rx_packets tx_bytes tx_packets teleported_goats
+0x0 1000 test0 1024 10 2048 20 2716057
+0x0000F00D00000000 1000 test0 512 5 512 5 3370318
diff --git a/services/tests/servicestests/res/raw/xt_qtaguid_typical b/services/tests/servicestests/res/raw/xt_qtaguid_typical
new file mode 100644
index 0000000..7c4f04e
--- /dev/null
+++ b/services/tests/servicestests/res/raw/xt_qtaguid_typical
@@ -0,0 +1,32 @@
+idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes
+1 wlan0 0x0 0 14615 4270
+2 wlan0 0x0 1000 5175 915
+3 wlan0 0x0 1021 3381 903
+4 wlan0 0x0 10004 333821 53558
+5 wlan0 0x0 10010 4888 37363
+6 wlan0 0x0 10013 52 104
+7 wlan0 0x74182ada00000000 10004 18725 1066
+8 rmnet0 0x0 0 301274 30244
+9 rmnet0 0x0 1000 304 441
+10 rmnet0 0x0 1013 2880 2272
+11 rmnet0 0x0 1021 31407 8430
+12 rmnet0 0x0 10003 32665 3814
+13 rmnet0 0x0 10004 2373141 420112
+14 rmnet0 0x0 10010 870370 1111727
+15 rmnet0 0x0 10013 240 240
+16 rmnet0 0x0 10016 16703 13512
+17 rmnet0 0x0 10017 3990 3269
+18 rmnet0 0x0 10018 474504 14516062
+19 rmnet0 0x0 10019 782804 71077
+20 rmnet0 0x0 10022 70671 49684
+21 rmnet0 0x0 10029 5785354 397159
+22 rmnet0 0x0 10033 2102 1686
+23 rmnet0 0x0 10034 15495464 227694
+24 rmnet0 0x0 10037 31184994 684122
+25 rmnet0 0x0 10051 298687 113485
+26 rmnet0 0x0 10056 29504 20669
+27 rmnet0 0x0 10069 683 596
+28 rmnet0 0x0 10072 34051 12453
+29 rmnet0 0x0 10077 7025393 213866
+30 rmnet0 0x0 10081 354 1178
+31 rmnet0 0x74182ada00000000 10037 28507378 437004
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
new file mode 100644
index 0000000..ac7cb5a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
+import static com.android.server.NetworkManagementSocketTagger.tagToKernel;
+
+import android.content.res.Resources;
+import android.net.NetworkStats;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.frameworks.servicestests.R;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+/**
+ * Tests for {@link NetworkManagementService}.
+ */
+@LargeTest
+public class NetworkManagementServiceTest extends AndroidTestCase {
+ private File mTestProc;
+ private NetworkManagementService mService;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mTestProc = getContext().getFilesDir();
+ mService = NetworkManagementService.createForTest(mContext, mTestProc);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mService = null;
+
+ super.tearDown();
+ }
+
+ public void testNetworkStatsDetail() throws Exception {
+ stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));
+
+ final NetworkStats stats = mService.getNetworkStatsDetail();
+ assertEquals(31, stats.size);
+ assertStatsEntry(stats, "wlan0", 0, 0, 14615L, 4270L);
+ assertStatsEntry(stats, "wlan0", 10004, 0, 333821L, 53558L);
+ assertStatsEntry(stats, "wlan0", 10004, 1947740890, 18725L, 1066L);
+ assertStatsEntry(stats, "rmnet0", 10037, 0, 31184994L, 684122L);
+ assertStatsEntry(stats, "rmnet0", 10037, 1947740890, 28507378L, 437004L);
+ }
+
+ public void testNetworkStatsDetailExtended() throws Exception {
+ stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats"));
+
+ final NetworkStats stats = mService.getNetworkStatsDetail();
+ assertEquals(2, stats.size);
+ assertStatsEntry(stats, "test0", 1000, 0, 1024L, 2048L);
+ assertStatsEntry(stats, "test0", 1000, 0xF00D, 512L, 512L);
+ }
+
+ public void testKernelTags() throws Exception {
+ assertEquals("0", tagToKernel(0x0));
+ assertEquals("214748364800", tagToKernel(0x32));
+ assertEquals("9223372032559808512", tagToKernel(Integer.MAX_VALUE));
+ assertEquals("0", tagToKernel(Integer.MIN_VALUE));
+ assertEquals("9223369837831520256", tagToKernel(Integer.MIN_VALUE - 512));
+
+ assertEquals(0, kernelToTag("0x0000000000000000"));
+ assertEquals(0x32, kernelToTag("0x0000003200000000"));
+ assertEquals(2147483647, kernelToTag("0x7fffffff00000000"));
+ assertEquals(0, kernelToTag("0x0000000000000000"));
+ assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000"));
+
+ }
+
+ /**
+ * Copy a {@link Resources#openRawResource(int)} into {@link File} for
+ * testing purposes.
+ */
+ private void stageFile(int rawId, File file) throws Exception {
+ new File(file.getParent()).mkdirs();
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = getContext().getResources().openRawResource(rawId);
+ out = new FileOutputStream(file);
+ Streams.copy(in, out);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ private static void assertStatsEntry(
+ NetworkStats stats, String iface, int uid, int tag, long rx, long tx) {
+ final int i = stats.findIndex(iface, uid, tag);
+ assertEquals(rx, stats.rx[i]);
+ assertEquals(tx, stats.tx[i]);
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 07e5425..b4ac987 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -41,7 +41,9 @@ import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.Signature;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.INetworkPolicyListener;
@@ -54,6 +56,7 @@ import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.os.Binder;
+import android.os.INetworkManagementService;
import android.os.IPowerManager;
import android.test.AndroidTestCase;
import android.test.mock.MockPackageManager;
@@ -63,12 +66,17 @@ import android.text.format.Time;
import android.util.TrustedTime;
import com.android.server.net.NetworkPolicyManagerService;
+import com.google.common.util.concurrent.AbstractFuture;
import org.easymock.Capture;
import org.easymock.EasyMock;
+import org.easymock.IAnswer;
import java.io.File;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Tests for {@link NetworkPolicyManagerService}.
@@ -88,6 +96,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
private IActivityManager mActivityManager;
private IPowerManager mPowerManager;
private INetworkStatsService mStatsService;
+ private INetworkManagementService mNetworkManagement;
private INetworkPolicyListener mPolicyListener;
private TrustedTime mTime;
private IConnectivityManager mConnManager;
@@ -118,22 +127,40 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
public String[] getPackagesForUid(int uid) {
return new String[] { "com.example" };
}
+
+ @Override
+ public PackageInfo getPackageInfo(String packageName, int flags) {
+ final PackageInfo info = new PackageInfo();
+ final Signature signature;
+ if ("android".equals(packageName)) {
+ signature = new Signature("F00D");
+ } else {
+ signature = new Signature("DEAD");
+ }
+ info.signatures = new Signature[] { signature };
+ return info;
+ }
};
}
};
mPolicyDir = getContext().getFilesDir();
+ for (File file : mPolicyDir.listFiles()) {
+ file.delete();
+ }
mActivityManager = createMock(IActivityManager.class);
mPowerManager = createMock(IPowerManager.class);
mStatsService = createMock(INetworkStatsService.class);
+ mNetworkManagement = createMock(INetworkManagementService.class);
mPolicyListener = createMock(INetworkPolicyListener.class);
mTime = createMock(TrustedTime.class);
mConnManager = createMock(IConnectivityManager.class);
mNotifManager = createMock(INotificationManager.class);
mService = new NetworkPolicyManagerService(
- mServiceContext, mActivityManager, mPowerManager, mStatsService, mTime, mPolicyDir);
+ mServiceContext, mActivityManager, mPowerManager, mStatsService,
+ mNetworkManagement, mTime, mPolicyDir);
mService.bindConnectivityManager(mConnManager);
mService.bindNotificationManager(mNotifManager);
@@ -152,6 +179,9 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
expectTime(System.currentTimeMillis());
+ // default behavior is background data enabled
+ expect(mConnManager.getBackgroundDataSetting()).andReturn(true);
+
replay();
mService.systemReady();
verifyAndReset();
@@ -228,81 +258,123 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
public void testScreenChangesRules() throws Exception {
- // push strict policy for foreground uid, verify ALLOW rule
- expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ Future<Void> future;
+
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+ future.get();
+ verifyAndReset();
+
+ // push strict policy for foreground uid, verify ALLOW rule
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+ future.get();
verifyAndReset();
// now turn screen off and verify REJECT rule
expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce();
- expectRulesChanged(UID_A, RULE_REJECT_METERED);
+ expectSetUidNetworkRules(UID_A, true);
+ future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
+ future.get();
verifyAndReset();
// and turn screen back on, verify ALLOW rule restored
expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
- expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
+ future.get();
verifyAndReset();
}
public void testPolicyNone() throws Exception {
+ Future<Void> future;
+
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ replay();
+ mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+ future.get();
+ verifyAndReset();
+
// POLICY_NONE should RULE_ALLOW in foreground
- expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mService.setUidPolicy(UID_A, POLICY_NONE);
- mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+ future.get();
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in background
- expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+ future.get();
verifyAndReset();
}
public void testPolicyReject() throws Exception {
+ Future<Void> future;
+
// POLICY_REJECT should RULE_ALLOW in background
- expectRulesChanged(UID_A, RULE_REJECT_METERED);
+ expectSetUidNetworkRules(UID_A, true);
+ future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+ future.get();
verifyAndReset();
// POLICY_REJECT should RULE_ALLOW in foreground
- expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+ future.get();
verifyAndReset();
// POLICY_REJECT should RULE_REJECT in background
- expectRulesChanged(UID_A, RULE_REJECT_METERED);
+ expectSetUidNetworkRules(UID_A, true);
+ future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+ future.get();
verifyAndReset();
}
public void testPolicyRejectAddRemove() throws Exception {
+ Future<Void> future;
+
// POLICY_NONE should have RULE_ALLOW in background
- expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
- mService.setUidPolicy(UID_A, POLICY_NONE);
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+ mService.setUidPolicy(UID_A, POLICY_NONE);
+ future.get();
verifyAndReset();
// adding POLICY_REJECT should cause RULE_REJECT
- expectRulesChanged(UID_A, RULE_REJECT_METERED);
+ expectSetUidNetworkRules(UID_A, true);
+ future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+ future.get();
verifyAndReset();
// removing POLICY_REJECT should return us to RULE_ALLOW
- expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mService.setUidPolicy(UID_A, POLICY_NONE);
+ future.get();
verifyAndReset();
}
@@ -350,6 +422,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
long elapsedRealtime = 0;
NetworkState[] state = null;
NetworkStats stats = null;
+ Future<Void> future;
final long TIME_FEB_15 = 1171497600000L;
final long TIME_MAR_10 = 1173484800000L;
@@ -360,10 +433,11 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
state = new NetworkState[] { buildWifi() };
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
expectTime(TIME_MAR_10 + elapsedRealtime);
- expectMeteredIfacesChanged();
+ future = expectMeteredIfacesChanged();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ future.get();
verifyAndReset();
// now change cycle to be on 15th, and test in early march, to verify we
@@ -377,30 +451,38 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
.andReturn(stats).atLeastOnce();
- // expect that quota remaining should be 1536 bytes
- // TODO: write up NetworkManagementService mock
+ // TODO: consider making strongly ordered mock
+ expectRemoveInterfaceQuota(TEST_IFACE);
+ expectSetInterfaceQuota(TEST_IFACE, 1536L);
expectClearNotifications();
- expectMeteredIfacesChanged(TEST_IFACE);
+ future = expectMeteredIfacesChanged(TEST_IFACE);
replay();
setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L));
+ future.get();
verifyAndReset();
}
public void testUidRemovedPolicyCleared() throws Exception {
+ Future<Void> future;
+
// POLICY_REJECT should RULE_REJECT in background
- expectRulesChanged(UID_A, RULE_REJECT_METERED);
+ expectSetUidNetworkRules(UID_A, true);
+ future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+ future.get();
verifyAndReset();
// uninstall should clear RULE_REJECT
- expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ expectSetUidNetworkRules(UID_A, false);
+ future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
final Intent intent = new Intent(ACTION_UID_REMOVED);
intent.putExtra(EXTRA_UID, UID_A);
mServiceContext.sendBroadcast(intent);
+ future.get();
verifyAndReset();
}
@@ -435,25 +517,62 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
expectLastCall().anyTimes();
}
- private void expectRulesChanged(int uid, int policy) throws Exception {
- mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
+ private void expectSetInterfaceQuota(String iface, long quota) throws Exception {
+ mNetworkManagement.setInterfaceQuota(iface, quota);
expectLastCall().atLeastOnce();
}
- private void expectMeteredIfacesChanged(String... ifaces) throws Exception {
- mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
+ private void expectRemoveInterfaceQuota(String iface) throws Exception {
+ mNetworkManagement.removeInterfaceQuota(iface);
expectLastCall().atLeastOnce();
}
+ private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
+ throws Exception {
+ mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+ expectLastCall().atLeastOnce();
+ }
+
+ private Future<Void> expectRulesChanged(int uid, int policy) throws Exception {
+ final FutureAnswer future = new FutureAnswer();
+ mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
+ expectLastCall().andAnswer(future);
+ return future;
+ }
+
+ private Future<Void> expectMeteredIfacesChanged(String... ifaces) throws Exception {
+ final FutureAnswer future = new FutureAnswer();
+ mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
+ expectLastCall().andAnswer(future);
+ return future;
+ }
+
+ private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> {
+ @Override
+ public Void get() throws InterruptedException, ExecutionException {
+ try {
+ return get(5, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Void answer() {
+ set(null);
+ return null;
+ }
+ }
+
private void replay() {
- EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
- mConnManager, mNotifManager);
+ EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
+ mNetworkManagement, mTime, mConnManager, mNotifManager);
}
private void verifyAndReset() {
- EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
- mConnManager, mNotifManager);
- EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
- mConnManager, mNotifManager);
+ EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
+ mNetworkManagement, mTime, mConnManager, mNotifManager);
+ EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
+ mNetworkManagement, mTime, mConnManager, mNotifManager);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 636d059..f2c28bb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -59,7 +59,6 @@ import android.os.INetworkManagementService;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
import android.util.TrustedTime;
import com.android.server.net.NetworkStatsService;