diff options
Diffstat (limited to 'services')
46 files changed, 6290 insertions, 4372 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 2b08ab59f87b..2702242f8d32 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1738,7 +1738,10 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this); tracksToRemove->add(track); // indicate to client process that the track was disabled because of underrun - cblk->flags |= CBLK_DISABLED_ON; + { + AutoMutex _l(cblk->lock); + cblk->flags |= CBLK_DISABLED_ON; + } } else if (mixerStatus != MIXER_TRACKS_READY) { mixerStatus = MIXER_TRACKS_ENABLED; } @@ -1787,10 +1790,9 @@ void AudioFlinger::MixerThread::invalidateTracks(int streamType) for (size_t i = 0; i < size; i++) { sp<Track> t = mTracks[i]; if (t->type() == streamType) { - t->mCblk->lock.lock(); + AutoMutex _lcblk(t->mCblk->lock); t->mCblk->flags |= CBLK_INVALID_ON; t->mCblk->cv.signal(); - t->mCblk->lock.unlock(); } } } @@ -2948,6 +2950,7 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const { if (mCblk->framesReady() >= mCblk->frameCount || (mCblk->flags & CBLK_FORCEREADY_MSK)) { + AutoMutex _l(mCblk->lock); mFillingUpStatus = FS_FILLED; mCblk->flags &= ~CBLK_FORCEREADY_MSK; return true; @@ -3063,19 +3066,18 @@ void AudioFlinger::PlaybackThread::Track::flush() // STOPPED state mState = STOPPED; - mCblk->lock.lock(); // NOTE: reset() will reset cblk->user and cblk->server with // the risk that at the same time, the AudioMixer is trying to read // data. In this case, getNextBuffer() would return a NULL pointer // as audio buffer => the AudioMixer code MUST always test that pointer // returned by getNextBuffer() is not NULL! reset(); - mCblk->lock.unlock(); } } void AudioFlinger::PlaybackThread::Track::reset() { + AutoMutex _l(mCblk->lock); // Do not reset twice to avoid discarding data written just after a flush and before // the audioflinger thread detects the track is stopped. if (!mResetDone) { @@ -3209,10 +3211,13 @@ void AudioFlinger::RecordThread::RecordTrack::stop() if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); recordThread->stop(this); - TrackBase::reset(); - // Force overerrun condition to avoid false overrun callback until first data is - // read from buffer - mCblk->flags |= CBLK_UNDERRUN_ON; + { + AutoMutex _l(mCblk->lock); + TrackBase::reset(); + // Force overerrun condition to avoid false overrun callback until first data is + // read from buffer + mCblk->flags |= CBLK_UNDERRUN_ON; + } } } diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index b52fc694e781..14f1e8b1dfc3 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -49,7 +49,6 @@ LOCAL_SHARED_LIBRARIES:= \ libcutils \ libmedia \ libcamera_client \ - libsurfaceflinger_client \ libgui LOCAL_MODULE:= libcameraservice diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index a09e16b3b412..f3c9959eb364 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -666,20 +666,6 @@ void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) { mHardware->releaseRecordingFrame(mem); } -int32_t CameraService::Client::getNumberOfVideoBuffers() const { - LOG1("getNumberOfVideoBuffers"); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return 0; - return mHardware->getNumberOfVideoBuffers(); -} - -sp<IMemory> CameraService::Client::getVideoBuffer(int32_t index) const { - LOG1("getVideoBuffer: %d", index); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return 0; - return mHardware->getVideoBuffer(index); -} - status_t CameraService::Client::storeMetaDataInBuffers(bool enabled) { LOG1("storeMetaDataInBuffers: %s", enabled? "true": "false"); @@ -938,7 +924,7 @@ void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, switch (msgType) { case CAMERA_MSG_SHUTTER: // ext1 is the dimension of the yuv picture. - client->handleShutter((image_rect_type *)ext1); + client->handleShutter(); break; default: client->handleGenericNotify(msgType, ext1, ext2); @@ -997,9 +983,7 @@ void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, } // snapshot taken callback -// "size" is the width and height of yuv picture for registerBuffer. -// If it is NULL, use the picture size from parameters. -void CameraService::Client::handleShutter(image_rect_type *size) { +void CameraService::Client::handleShutter(void) { if (mPlayShutterSound) { mCameraService->playSound(SOUND_SHUTTER); } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 1c43b00f7ff1..28e8cc0384c6 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -99,8 +99,6 @@ private: virtual status_t startPreview(); virtual void stopPreview(); virtual bool previewEnabled(); - virtual int32_t getNumberOfVideoBuffers() const; - virtual sp<IMemory> getVideoBuffer(int32_t index) const; virtual status_t storeMetaDataInBuffers(bool enabled); virtual status_t startRecording(); virtual void stopRecording(); @@ -152,7 +150,7 @@ private: // convert client from cookie static sp<Client> getClientFromCookie(void* user); // handlers for messages - void handleShutter(image_rect_type *size); + void handleShutter(void); void handlePreviewData(const sp<IMemory>& mem); void handlePostview(const sp<IMemory>& mem); void handleRawPicture(const sp<IMemory>& mem); diff --git a/services/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk index cf4e42ff8742..cf7302a76d30 100644 --- a/services/camera/tests/CameraServiceTest/Android.mk +++ b/services/camera/tests/CameraServiceTest/Android.mk @@ -19,7 +19,7 @@ LOCAL_SHARED_LIBRARIES += \ libutils \ libui \ libcamera_client \ - libsurfaceflinger_client + libgui # Disable it because the ISurface interface may change, and before we have a # chance to fix this test, we don't want to break normal builds. diff --git a/services/input/Android.mk b/services/input/Android.mk index d7b61fc9592e..96431bcd9319 100644 --- a/services/input/Android.mk +++ b/services/input/Android.mk @@ -29,8 +29,8 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libhardware \ libhardware_legacy \ - libsurfaceflinger_client \ libskia \ + libgui \ libui LOCAL_C_INCLUDES := \ diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 853dda42e030..7625adb14108 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -127,9 +127,11 @@ EventHub::EventHub(void) : mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1), mOpeningDevices(0), mClosingDevices(0), mOpened(false), mNeedToSendFinishedDeviceScan(false), - mInputBufferIndex(0), mInputBufferCount(0), mInputFdIndex(0) { + mInputFdIndex(1) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + memset(mSwitches, 0, sizeof(mSwitches)); + mNumCpus = sysconf(_SC_NPROCESSORS_ONLN); } EventHub::~EventHub(void) { @@ -445,17 +447,10 @@ EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { return NULL; } -bool EventHub::getEvent(RawEvent* outEvent) { - outEvent->deviceId = 0; - outEvent->type = 0; - outEvent->scanCode = 0; - outEvent->keyCode = 0; - outEvent->flags = 0; - outEvent->value = 0; - outEvent->when = 0; - - // Note that we only allow one caller to getEvent(), so don't need +size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { + // Note that we only allow one caller to getEvents(), so don't need // to do locking here... only when adding/removing devices. + assert(bufferSize >= 1); if (!mOpened) { mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; @@ -463,99 +458,62 @@ bool EventHub::getEvent(RawEvent* outEvent) { mNeedToSendFinishedDeviceScan = true; } + struct input_event readBuffer[bufferSize]; + + RawEvent* event = buffer; + size_t capacity = bufferSize; for (;;) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + // Report any devices that had last been added/removed. - if (mClosingDevices != NULL) { + while (mClosingDevices) { Device* device = mClosingDevices; LOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.string()); mClosingDevices = device->next; - if (device->id == mBuiltInKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = DEVICE_REMOVED; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); + event->when = now; + event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + event->type = DEVICE_REMOVED; + event += 1; delete device; mNeedToSendFinishedDeviceScan = true; - return true; + if (--capacity == 0) { + break; + } } - if (mOpeningDevices != NULL) { + while (mOpeningDevices != NULL) { Device* device = mOpeningDevices; LOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.string()); mOpeningDevices = device->next; - if (device->id == mBuiltInKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = DEVICE_ADDED; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); + event->when = now; + event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + event->type = DEVICE_ADDED; + event += 1; mNeedToSendFinishedDeviceScan = true; - return true; + if (--capacity == 0) { + break; + } } if (mNeedToSendFinishedDeviceScan) { mNeedToSendFinishedDeviceScan = false; - outEvent->type = FINISHED_DEVICE_SCAN; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - return true; + event->when = now; + event->type = FINISHED_DEVICE_SCAN; + event += 1; + if (--capacity == 0) { + break; + } } // Grab the next input event. + // mInputFdIndex is initially 1 because index 0 is used for inotify. bool deviceWasRemoved = false; - for (;;) { - // Consume buffered input events, if any. - if (mInputBufferIndex < mInputBufferCount) { - const struct input_event& iev = mInputBufferData[mInputBufferIndex++]; - const Device* device = mDevices[mInputFdIndex]; - - LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(), - (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); - if (device->id == mBuiltInKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = iev.type; - outEvent->scanCode = iev.code; - outEvent->flags = 0; - if (iev.type == EV_KEY) { - outEvent->keyCode = AKEYCODE_UNKNOWN; - if (device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code, - &outEvent->keyCode, &outEvent->flags); - LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", - iev.code, outEvent->keyCode, outEvent->flags, err); - } - } else { - outEvent->keyCode = iev.code; - } - outEvent->value = iev.value; - - // Use an event timestamp in the same timebase as - // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis() - // as expected by the rest of the system. - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - return true; - } - - // Finish reading all events from devices identified in previous poll(). - // This code assumes that mInputDeviceIndex is initially 0 and that the - // revents member of pollfd is initialized to 0 when the device is first added. - // Since mFds[0] is used for inotify, we process regular events starting at index 1. - mInputFdIndex += 1; - if (mInputFdIndex >= mFds.size()) { - break; - } - + while (mInputFdIndex < mFds.size()) { const struct pollfd& pfd = mFds[mInputFdIndex]; if (pfd.revents & POLLIN) { - int32_t readSize = read(pfd.fd, mInputBufferData, - sizeof(struct input_event) * INPUT_BUFFER_SIZE); + int32_t readSize = read(pfd.fd, readBuffer, sizeof(struct input_event) * capacity); if (readSize < 0) { if (errno == ENODEV) { deviceWasRemoved = true; @@ -566,11 +524,43 @@ bool EventHub::getEvent(RawEvent* outEvent) { } } else if ((readSize % sizeof(struct input_event)) != 0) { LOGE("could not get event (wrong size: %d)", readSize); + } else if (readSize == 0) { // eof + deviceWasRemoved = true; + break; } else { - mInputBufferCount = size_t(readSize) / sizeof(struct input_event); - mInputBufferIndex = 0; + const Device* device = mDevices[mInputFdIndex]; + int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + + size_t count = size_t(readSize) / sizeof(struct input_event); + for (size_t i = 0; i < count; i++) { + const struct input_event& iev = readBuffer[i]; + LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d", + device->path.string(), + (int) iev.time.tv_sec, (int) iev.time.tv_usec, + iev.type, iev.code, iev.value); + + event->when = now; + event->deviceId = deviceId; + event->type = iev.type; + event->scanCode = iev.code; + event->value = iev.value; + event->keyCode = AKEYCODE_UNKNOWN; + event->flags = 0; + if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) { + status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code, + &event->keyCode, &event->flags); + LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", + iev.code, event->keyCode, event->flags, err); + } + event += 1; + } + capacity -= count; + if (capacity == 0) { + break; + } } } + mInputFdIndex += 1; } // Handle the case where a device has been removed but INotify has not yet noticed. @@ -586,10 +576,16 @@ bool EventHub::getEvent(RawEvent* outEvent) { if(mFds[0].revents & POLLIN) { readNotify(mFds[0].fd); mFds.editItemAt(0).revents = 0; + mInputFdIndex = mFds.size(); continue; // report added or removed devices immediately } #endif + // Return now if we have collected any events, otherwise poll. + if (event != buffer) { + break; + } + // Poll for events. Mind the wake lock dance! // We hold a wake lock at all times except during poll(). This works due to some // subtle choreography. When a device driver has pending (unread) events, it acquires @@ -598,22 +594,46 @@ bool EventHub::getEvent(RawEvent* outEvent) { // when this happens, the EventHub holds onto its own user wake lock while the client // is processing events. Thus the system can only sleep if there are no events // pending or currently being processed. + // + // The timeout is advisory only. If the device is asleep, it will not wake just to + // service the timeout. release_wake_lock(WAKE_LOCK_ID); - int pollResult = poll(mFds.editArray(), mFds.size(), -1); + int pollResult = poll(mFds.editArray(), mFds.size(), timeoutMillis); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); - if (pollResult <= 0) { + if (pollResult == 0) { + break; // timed out + } + if (pollResult < 0) { + // Sleep after errors to avoid locking up the system. + // Hopefully the error is transient. if (errno != EINTR) { LOGW("poll failed (errno=%d)\n", errno); usleep(100000); } + } else { + // On an SMP system, it is possible for the framework to read input events + // faster than the kernel input device driver can produce a complete packet. + // Because poll() wakes up as soon as the first input event becomes available, + // the framework will often end up reading one event at a time until the + // packet is complete. Instead of one call to read() returning 71 events, + // it could take 71 calls to read() each returning 1 event. + // + // Sleep for a short period of time after waking up from the poll() to give + // the kernel time to finish writing the entire packet of input events. + if (mNumCpus > 1) { + usleep(250); + } } // Prepare to process all of the FDs we just polled. - mInputFdIndex = 0; + mInputFdIndex = 1; } + + // All done, return the number of events we read. + return event - buffer; } /* diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 7053a948049c..4d26a9565efd 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -157,6 +157,8 @@ public: // Sent when all added/removed devices from the most recent scan have been reported. // This event is always sent at least once. FINISHED_DEVICE_SCAN = 0x30000000, + + FIRST_SYNTHETIC_EVENT = DEVICE_ADDED, }; virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0; @@ -181,13 +183,18 @@ public: virtual void addExcludedDevice(const char* deviceName) = 0; /* - * Wait for the next event to become available and return it. + * Wait for events to become available and returns them. * After returning, the EventHub holds onto a wake lock until the next call to getEvent. * This ensures that the device will not go to sleep while the event is being processed. * If the device needs to remain awake longer than that, then the caller is responsible * for taking care of it (say, by poking the power manager user activity timer). + * + * The timeout is advisory only. If the device is asleep, it will not wake just to + * service the timeout. + * + * Returns the number of events obtained, or 0 if the timeout expired. */ - virtual bool getEvent(RawEvent* outEvent) = 0; + virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0; /* * Query current input state. @@ -244,7 +251,7 @@ public: virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; - virtual bool getEvent(RawEvent* outEvent); + virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize); virtual bool hasLed(int32_t deviceId, int32_t led) const; virtual void setLedState(int32_t deviceId, int32_t led, bool on); @@ -331,11 +338,11 @@ private: // device ids that report particular switches. int32_t mSwitches[SW_MAX + 1]; - static const int INPUT_BUFFER_SIZE = 64; - struct input_event mInputBufferData[INPUT_BUFFER_SIZE]; - size_t mInputBufferIndex; - size_t mInputBufferCount; + // The index of the next file descriptor that needs to be read. size_t mInputFdIndex; + + // Set to the number of CPUs. + int32_t mNumCpus; }; }; // namespace android diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 19295e6d77ca..feca58d4ff79 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -48,6 +48,9 @@ // Log debug messages about the app switch latency optimization. #define DEBUG_APP_SWITCH 0 +// Log debug messages about hover events. +#define DEBUG_HOVER 0 + #include "InputDispatcher.h" #include <cutils/log.h> @@ -115,7 +118,9 @@ static bool isValidMotionAction(int32_t action, size_t pointerCount) { case AMOTION_EVENT_ACTION_CANCEL: case AMOTION_EVENT_ACTION_MOVE: case AMOTION_EVENT_ACTION_OUTSIDE: + case AMOTION_EVENT_ACTION_HOVER_ENTER: case AMOTION_EVENT_ACTION_HOVER_MOVE: + case AMOTION_EVENT_ACTION_HOVER_EXIT: case AMOTION_EVENT_ACTION_SCROLL: return true; case AMOTION_EVENT_ACTION_POINTER_DOWN: @@ -185,7 +190,8 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mFocusedWindow(NULL), mFocusedApplication(NULL), mCurrentInputTargetsValid(false), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { + mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE), + mLastHoverWindow(NULL) { mLooper = new Looper(false); mInboundQueue.headSentinel.refCount = -1; @@ -238,15 +244,7 @@ void InputDispatcher::dispatchOnce() { // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t currentTime = now(); - int32_t timeoutMillis; - if (nextWakeupTime > currentTime) { - uint64_t timeout = uint64_t(nextWakeupTime - currentTime); - timeout = (timeout + 999999LL) / 1000000LL; - timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout); - } else { - timeoutMillis = 0; - } - + int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); mLooper->pollOnce(timeoutMillis); } @@ -845,10 +843,11 @@ bool InputDispatcher::dispatchMotionLocked( bool conflictingPointerActions = false; if (! mCurrentInputTargetsValid) { int32_t injectionResult; + const MotionSample* splitBatchAfterSample = NULL; if (isPointerEvent) { // Pointer event. (eg. touchscreen) injectionResult = findTouchedWindowTargetsLocked(currentTime, - entry, nextWakeupTime, &conflictingPointerActions); + entry, nextWakeupTime, &conflictingPointerActions, &splitBatchAfterSample); } else { // Non touch event. (eg. trackball) injectionResult = findFocusedWindowTargetsLocked(currentTime, @@ -865,6 +864,41 @@ bool InputDispatcher::dispatchMotionLocked( addMonitoringTargetsLocked(); commitTargetsLocked(); + + // Unbatch the event if necessary by splitting it into two parts after the + // motion sample indicated by splitBatchAfterSample. + if (splitBatchAfterSample && splitBatchAfterSample->next) { +#if DEBUG_BATCHING + uint32_t originalSampleCount = entry->countSamples(); +#endif + MotionSample* nextSample = splitBatchAfterSample->next; + MotionEntry* nextEntry = mAllocator.obtainMotionEntry(nextSample->eventTime, + entry->deviceId, entry->source, entry->policyFlags, + entry->action, entry->flags, entry->metaState, entry->edgeFlags, + entry->xPrecision, entry->yPrecision, entry->downTime, + entry->pointerCount, entry->pointerIds, nextSample->pointerCoords); + if (nextSample != entry->lastSample) { + nextEntry->firstSample.next = nextSample->next; + nextEntry->lastSample = entry->lastSample; + } + mAllocator.freeMotionSample(nextSample); + + entry->lastSample = const_cast<MotionSample*>(splitBatchAfterSample); + entry->lastSample->next = NULL; + + if (entry->injectionState) { + nextEntry->injectionState = entry->injectionState; + entry->injectionState->refCount += 1; + } + +#if DEBUG_BATCHING + LOGD("Split batch of %d samples into two parts, first part has %d samples, " + "second part has %d samples.", originalSampleCount, + entry->countSamples(), nextEntry->countSamples()); +#endif + + mInboundQueue.enqueueAtHead(nextEntry); + } } // Dispatch the motion. @@ -1115,7 +1149,8 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0)); + addWindowTargetLocked(mFocusedWindow, + InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0)); // Done. Failed: @@ -1132,7 +1167,8 @@ Unresponsive: } int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry* entry, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { + const MotionEntry* entry, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions, + const MotionSample** outSplitBatchAfterSample) { enum InjectionPermission { INJECTION_PERMISSION_UNKNOWN, INJECTION_PERMISSION_GRANTED, @@ -1175,14 +1211,19 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Update the touch state as needed based on the properties of the touch event. int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; + const InputWindow* newHoverWindow = NULL; bool isSplit = mTouchState.split; bool wrongDevice = mTouchState.down && (mTouchState.deviceId != entry->deviceId || mTouchState.source != entry->source); - if (maskedAction == AMOTION_EVENT_ACTION_DOWN - || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE - || maskedAction == AMOTION_EVENT_ACTION_SCROLL) { + bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE + || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER + || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); + bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN + || maskedAction == AMOTION_EVENT_ACTION_SCROLL + || isHoverAction); + if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; if (wrongDevice && !down) { mTempTouchState.copyFrom(mTouchState); @@ -1198,26 +1239,25 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, mTempTouchState.copyFrom(mTouchState); } if (wrongDevice) { -#if DEBUG_INPUT_DISPATCHER_POLICY +#if DEBUG_FOCUS LOGD("Dropping event because a pointer for a different device is already down."); #endif injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } - if (maskedAction == AMOTION_EVENT_ACTION_DOWN - || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) - || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE - || maskedAction == AMOTION_EVENT_ACTION_SCROLL) { + if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ + const MotionSample* sample = &entry->firstSample; int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex]. + int32_t x = int32_t(sample->pointerCoords[pointerIndex]. getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex]. + int32_t y = int32_t(sample->pointerCoords[pointerIndex]. getAxisValue(AMOTION_EVENT_AXIS_Y)); const InputWindow* newTouchedWindow = NULL; const InputWindow* topErrorWindow = NULL; + bool isTouchModal = false; // Traverse windows from front to back to find touched window and outside targets. size_t numWindows = mWindows.size(); @@ -1233,7 +1273,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (window->visible) { if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) { - bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE + isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0; if (isTouchModal || window->touchableRegionContainsPoint(x, y)) { if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) { @@ -1245,7 +1285,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (maskedAction == AMOTION_EVENT_ACTION_DOWN && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) { - int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE; + int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE; if (isWindowObscuredAtPointLocked(window, x, y)) { outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } @@ -1298,7 +1338,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Set target flags. - int32_t targetFlags = InputTarget::FLAG_FOREGROUND; + int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS; if (isSplit) { targetFlags |= InputTarget::FLAG_SPLIT; } @@ -1306,6 +1346,28 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } + // Update hover state. + if (isHoverAction) { + newHoverWindow = newTouchedWindow; + + // Ensure all subsequent motion samples are also within the touched window. + // Set *outSplitBatchAfterSample to the sample before the first one that is not + // within the touched window. + if (!isTouchModal) { + while (sample->next) { + if (!newHoverWindow->touchableRegionContainsPoint( + sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), + sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y))) { + *outSplitBatchAfterSample = sample; + break; + } + sample = sample->next; + } + } + } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { + newHoverWindow = mLastHoverWindow; + } + // Update the temporary touch state. BitSet32 pointerIds; if (isSplit) { @@ -1318,7 +1380,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // If the pointer is not currently down, then ignore the event. if (! mTempTouchState.down) { -#if DEBUG_INPUT_DISPATCHER_POLICY +#if DEBUG_FOCUS LOGD("Dropping event because the pointer is not down or we previously " "dropped the pointer down event."); #endif @@ -1327,6 +1389,29 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } } + if (newHoverWindow != mLastHoverWindow) { + // Split the batch here so we send exactly one sample as part of ENTER or EXIT. + *outSplitBatchAfterSample = &entry->firstSample; + + // Let the previous window know that the hover sequence is over. + if (mLastHoverWindow) { +#if DEBUG_HOVER + LOGD("Sending hover exit event to window %s.", mLastHoverWindow->name.string()); +#endif + mTempTouchState.addOrUpdateWindow(mLastHoverWindow, + InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); + } + + // Let the new window know that the hover sequence is starting. + if (newHoverWindow) { +#if DEBUG_HOVER + LOGD("Sending hover enter event to window %s.", newHoverWindow->name.string()); +#endif + mTempTouchState.addOrUpdateWindow(newHoverWindow, + InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0)); + } + } + // Check permission to inject into all touched foreground windows and ensure there // is at least one touched foreground window. { @@ -1343,7 +1428,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } } if (! haveForegroundWindow) { -#if DEBUG_INPUT_DISPATCHER_POLICY +#if DEBUG_FOCUS LOGD("Dropping event because there is no touched foreground window to receive it."); #endif injectionResult = INPUT_EVENT_INJECTION_FAILED; @@ -1360,7 +1445,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { // If the touched window is paused then keep waiting. if (touchedWindow.window->paused) { -#if DEBUG_INPUT_DISPATCHER_POLICY +#if DEBUG_FOCUS LOGD("Waiting because touched window is paused."); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, @@ -1393,7 +1478,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const InputWindow* window = & mWindows[i]; if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) { mTempTouchState.addOrUpdateWindow(window, - InputTarget::FLAG_WINDOW_IS_OBSCURED, BitSet32(0)); + InputTarget::FLAG_WINDOW_IS_OBSCURED + | InputTarget::FLAG_DISPATCH_AS_IS, + BitSet32(0)); } } } @@ -1408,8 +1495,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, touchedWindow.pointerIds); } - // Drop the outside touch window since we will not care about them in the next iteration. - mTempTouchState.removeOutsideTouchWindows(); + // Drop the outside or hover touch windows since we will not care about them + // in the next iteration. + mTempTouchState.filterNonAsIsTouchWindows(); Failed: // Check injection permission once and for all. @@ -1426,7 +1514,7 @@ Failed: if (!wrongDevice) { if (maskedAction == AMOTION_EVENT_ACTION_UP || maskedAction == AMOTION_EVENT_ACTION_CANCEL - || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { + || isHoverAction) { // All pointers up or canceled. mTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { @@ -1463,6 +1551,9 @@ Failed: // Save changes to touch state as-is for all other actions. mTouchState.copyFrom(mTempTouchState); } + + // Update hover state. + mLastHoverWindow = newHoverWindow; } } else { #if DEBUG_FOCUS @@ -1728,10 +1819,36 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } } + // Enqueue dispatch entries for the requested modes. + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_OUTSIDE); + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); + enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, + resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_IS); + + // If the outbound queue was previously empty, start the dispatch cycle going. + if (wasEmpty) { + activateConnectionLocked(connection.get()); + startDispatchCycleLocked(currentTime, connection); + } +} + +void InputDispatcher::enqueueDispatchEntryLocked( + const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, + bool resumeWithAppendedMotionSample, int32_t dispatchMode) { + int32_t inputTargetFlags = inputTarget->flags; + if (!(inputTargetFlags & dispatchMode)) { + return; + } + inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode; + // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref - inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset); + inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset); if (dispatchEntry->hasForegroundTarget()) { incrementPendingForegroundDispatchesLocked(eventEntry); } @@ -1752,12 +1869,6 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry); - - // If the outbound queue was previously empty, start the dispatch cycle going. - if (wasEmpty) { - activateConnectionLocked(connection.get()); - startDispatchCycleLocked(currentTime, connection); - } } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, @@ -1776,12 +1887,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Mark the dispatch entry as in progress. dispatchEntry->inProgress = true; - // Update the connection's input state. - EventEntry* eventEntry = dispatchEntry->eventEntry; - connection->inputState.trackEvent(eventEntry); - // Publish the event. status_t status; + EventEntry* eventEntry = dispatchEntry->eventEntry; switch (eventEntry->type) { case EventEntry::TYPE_KEY: { KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); @@ -1790,6 +1898,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, int32_t action = keyEntry->action; int32_t flags = keyEntry->flags; + // Update the connection's input state. + connection->inputState.trackKey(keyEntry, action); + // Publish the key event. status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source, action, flags, keyEntry->keyCode, keyEntry->scanCode, @@ -1811,8 +1922,12 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Apply target flags. int32_t action = motionEntry->action; int32_t flags = motionEntry->flags; - if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) { + if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { action = AMOTION_EVENT_ACTION_OUTSIDE; + } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { + action = AMOTION_EVENT_ACTION_HOVER_EXIT; + } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { + action = AMOTION_EVENT_ACTION_HOVER_ENTER; } if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; @@ -1837,6 +1952,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, yOffset = 0.0f; } + // Update the connection's input state. + connection->inputState.trackMotion(motionEntry, action); + // Publish the motion event and the first motion sample. status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId, motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState, @@ -1853,31 +1971,34 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, return; } - // Append additional motion samples. - MotionSample* nextMotionSample = firstMotionSample->next; - for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { - status = connection->inputPublisher.appendMotionSample( - nextMotionSample->eventTime, nextMotionSample->pointerCoords); - if (status == NO_MEMORY) { + if (action == AMOTION_EVENT_ACTION_MOVE + || action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + // Append additional motion samples. + MotionSample* nextMotionSample = firstMotionSample->next; + for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { + status = connection->inputPublisher.appendMotionSample( + nextMotionSample->eventTime, nextMotionSample->pointerCoords); + if (status == NO_MEMORY) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will " "be sent in the next dispatch cycle.", connection->getInputChannelName()); #endif - break; - } - if (status != OK) { - LOGE("channel '%s' ~ Could not append motion sample " - "for a reason other than out of memory, status=%d", - connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection); - return; + break; + } + if (status != OK) { + LOGE("channel '%s' ~ Could not append motion sample " + "for a reason other than out of memory, status=%d", + connection->getInputChannelName(), status); + abortBrokenDispatchCycleLocked(currentTime, connection); + return; + } } - } - // Remember the next motion sample that we could not dispatch, in case we ran out - // of space in the shared memory buffer. - dispatchEntry->tailMotionSample = nextMotionSample; + // Remember the next motion sample that we could not dispatch, in case we ran out + // of space in the shared memory buffer. + dispatchEntry->tailMotionSample = nextMotionSample; + } break; } @@ -2135,8 +2256,8 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet if (pointerIds.hasBit(pointerId)) { splitPointerIndexMap[splitPointerCount] = originalPointerIndex; splitPointerIds[splitPointerCount] = pointerId; - splitPointerCoords[splitPointerCount] = - originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]; + splitPointerCoords[splitPointerCount].copyFrom( + originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]); splitPointerCount += 1; } } @@ -2199,14 +2320,19 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount; splitPointerIndex++) { uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex]; - splitPointerCoords[splitPointerIndex] = - originalMotionSample->pointerCoords[originalPointerIndex]; + splitPointerCoords[splitPointerIndex].copyFrom( + originalMotionSample->pointerCoords[originalPointerIndex]); } mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime, splitPointerCoords); } + if (originalMotionEntry->injectionState) { + splitMotionEntry->injectionState = originalMotionEntry->injectionState; + splitMotionEntry->injectionState->refCount += 1; + } + return splitMotionEntry; } @@ -2416,6 +2542,29 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t continue; } + if (action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + if (!mLastHoverWindow) { +#if DEBUG_BATCHING + LOGD("Not streaming hover move because there is no " + "last hovered window."); +#endif + goto NoBatchingOrStreaming; + } + + const InputWindow* hoverWindow = findTouchedWindowAtLocked( + pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), + pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + if (mLastHoverWindow != hoverWindow) { +#if DEBUG_BATCHING + LOGD("Not streaming hover move because the last hovered window " + "is '%s' but the currently hovered window is '%s'.", + mLastHoverWindow->name.string(), + hoverWindow ? hoverWindow->name.string() : "<null>"); +#endif + goto NoBatchingOrStreaming; + } + } + // Hurray! This foreground target is currently dispatching a move event // that we can stream onto. Append the motion sample and resume dispatch. mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); @@ -2694,6 +2843,11 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { oldFocusedWindowChannel = mFocusedWindow->inputChannel; mFocusedWindow = NULL; } + sp<InputChannel> oldLastHoverWindowChannel; + if (mLastHoverWindow) { + oldLastHoverWindowChannel = mLastHoverWindow->inputChannel; + mLastHoverWindow = NULL; + } mWindows.clear(); @@ -2744,6 +2898,12 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { } } + // Recover the last hovered window. + if (oldLastHoverWindowChannel != NULL) { + mLastHoverWindow = getWindowLocked(oldLastHoverWindowChannel); + oldLastHoverWindowChannel.clear(); + } + #if DEBUG_FOCUS //logDispatchStateLocked(); #endif @@ -2853,7 +3013,8 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel, mTouchState.windows.removeAt(i); int32_t newTargetFlags = oldTargetFlags - & (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT); + & (InputTarget::FLAG_FOREGROUND + | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); mTouchState.addOrUpdateWindow(toWindow, newTargetFlags, pointerIds); found = true; @@ -3453,7 +3614,7 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec entry->lastSample = & entry->firstSample; for (uint32_t i = 0; i < pointerCount; i++) { entry->pointerIds[i] = pointerIds[i]; - entry->firstSample.pointerCoords[i] = pointerCoords[i]; + entry->firstSample.pointerCoords[i].copyFrom(pointerCoords[i]); } return entry; } @@ -3541,6 +3702,10 @@ void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) { } } +void InputDispatcher::Allocator::freeMotionSample(MotionSample* sample) { + mMotionSamplePool.free(sample); +} + void InputDispatcher::Allocator::releaseDispatchEntry(DispatchEntry* entry) { releaseEventEntry(entry->eventEntry); mDispatchEntryPool.free(entry); @@ -3556,7 +3721,7 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, sample->eventTime = eventTime; uint32_t pointerCount = motionEntry->pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { - sample->pointerCoords[i] = pointerCoords[i]; + sample->pointerCoords[i].copyFrom(pointerCoords[i]); } sample->next = NULL; @@ -3596,22 +3761,19 @@ bool InputDispatcher::InputState::isNeutral() const { return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); } -void InputDispatcher::InputState::trackEvent( - const EventEntry* entry) { +void InputDispatcher::InputState::trackEvent(const EventEntry* entry, int32_t action) { switch (entry->type) { case EventEntry::TYPE_KEY: - trackKey(static_cast<const KeyEntry*>(entry)); + trackKey(static_cast<const KeyEntry*>(entry), action); break; case EventEntry::TYPE_MOTION: - trackMotion(static_cast<const MotionEntry*>(entry)); + trackMotion(static_cast<const MotionEntry*>(entry), action); break; } } -void InputDispatcher::InputState::trackKey( - const KeyEntry* entry) { - int32_t action = entry->action; +void InputDispatcher::InputState::trackKey(const KeyEntry* entry, int32_t action) { for (size_t i = 0; i < mKeyMementos.size(); i++) { KeyMemento& memento = mKeyMementos.editItemAt(i); if (memento.deviceId == entry->deviceId @@ -3646,17 +3808,18 @@ Found: } } -void InputDispatcher::InputState::trackMotion( - const MotionEntry* entry) { - int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK; +void InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action) { + int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; for (size_t i = 0; i < mMotionMementos.size(); i++) { MotionMemento& memento = mMotionMementos.editItemAt(i); if (memento.deviceId == entry->deviceId && memento.source == entry->source) { - switch (action) { + switch (actionMasked) { case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_HOVER_ENTER: case AMOTION_EVENT_ACTION_HOVER_MOVE: + case AMOTION_EVENT_ACTION_HOVER_EXIT: mMotionMementos.removeAt(i); return; @@ -3677,7 +3840,11 @@ void InputDispatcher::InputState::trackMotion( } Found: - if (action == AMOTION_EVENT_ACTION_DOWN) { + switch (actionMasked) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + case AMOTION_EVENT_ACTION_HOVER_EXIT: mMotionMementos.push(); MotionMemento& memento = mMotionMementos.editTop(); memento.deviceId = entry->deviceId; @@ -3686,6 +3853,7 @@ Found: memento.yPrecision = entry->yPrecision; memento.downTime = entry->downTime; memento.setPointers(entry); + memento.hovering = actionMasked != AMOTION_EVENT_ACTION_DOWN; } } @@ -3693,7 +3861,7 @@ void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* pointerCount = entry->pointerCount; for (uint32_t i = 0; i < entry->pointerCount; i++) { pointerIds[i] = entry->pointerIds[i]; - pointerCoords[i] = entry->lastSample->pointerCoords[i]; + pointerCoords[i].copyFrom(entry->lastSample->pointerCoords[i]); } } @@ -3718,7 +3886,10 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim if (shouldCancelMotion(memento, options)) { outEvents.push(allocator->obtainMotionEntry(currentTime, memento.deviceId, memento.source, 0, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0, + memento.hovering + ? AMOTION_EVENT_ACTION_HOVER_EXIT + : AMOTION_EVENT_ACTION_CANCEL, + 0, 0, 0, memento.xPrecision, memento.yPrecision, memento.downTime, memento.pointerCount, memento.pointerIds, memento.pointerCoords)); mMotionMementos.removeAt(i); @@ -3884,12 +4055,15 @@ void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window, touchedWindow.channel = window->inputChannel; } -void InputDispatcher::TouchState::removeOutsideTouchWindows() { +void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { for (size_t i = 0 ; i < windows.size(); ) { - if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) { - windows.removeAt(i); - } else { + TouchedWindow& window = windows.editItemAt(i); + if (window.targetFlags & InputTarget::FLAG_DISPATCH_AS_IS) { + window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; + window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; i += 1; + } else { + windows.removeAt(i); } } } diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h index 1e118c4d2891..59c5298e68dd 100644 --- a/services/input/InputDispatcher.h +++ b/services/input/InputDispatcher.h @@ -86,20 +86,40 @@ enum { struct InputTarget { enum { /* This flag indicates that the event is being delivered to a foreground application. */ - FLAG_FOREGROUND = 0x01, - - /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside - * of the area of this target and so should instead be delivered as an - * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ - FLAG_OUTSIDE = 0x02, + FLAG_FOREGROUND = 1 << 0, /* This flag indicates that the target of a MotionEvent is partly or wholly * obscured by another visible window above it. The motion event should be * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - FLAG_WINDOW_IS_OBSCURED = 0x04, + FLAG_WINDOW_IS_OBSCURED = 1 << 1, /* This flag indicates that a motion event is being split across multiple windows. */ - FLAG_SPLIT = 0x08, + FLAG_SPLIT = 1 << 2, + + /* This flag indicates that the event should be sent as is. + * Should always be set unless the event is to be transmuted. */ + FLAG_DISPATCH_AS_IS = 1 << 8, + + /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside + * of the area of this target and so should instead be delivered as an + * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ + FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, + + /* This flag indicates that a hover sequence is starting in the given window. + * The event is transmuted into ACTION_HOVER_ENTER. */ + FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, + + /* This flag indicates that a hover event happened outside of a window which handled + * previous hover events, signifying the end of the current hover sequence for that + * window. + * The event is transmuted into ACTION_HOVER_ENTER. */ + FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, + + /* Mask for all dispatch modes. */ + FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS + | FLAG_DISPATCH_AS_OUTSIDE + | FLAG_DISPATCH_AS_HOVER_ENTER + | FLAG_DISPATCH_AS_HOVER_EXIT, }; // The input channel to be targeted. @@ -567,6 +587,7 @@ private: void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry); void releaseKeyEntry(KeyEntry* entry); void releaseMotionEntry(MotionEntry* entry); + void freeMotionSample(MotionSample* sample); void releaseDispatchEntry(DispatchEntry* entry); void releaseCommandEntry(CommandEntry* entry); @@ -608,13 +629,13 @@ private: bool isNeutral() const; // Records tracking information for an event that has just been published. - void trackEvent(const EventEntry* entry); + void trackEvent(const EventEntry* entry, int32_t action); // Records tracking information for a key event that has just been published. - void trackKey(const KeyEntry* entry); + void trackKey(const KeyEntry* entry, int32_t action); // Records tracking information for a motion event that has just been published. - void trackMotion(const MotionEntry* entry); + void trackMotion(const MotionEntry* entry, int32_t action); // Synthesizes cancelation events for the current state and resets the tracked state. void synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator, @@ -645,6 +666,7 @@ private: uint32_t pointerCount; int32_t pointerIds[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; + bool hovering; void setPointers(const MotionEntry* entry); }; @@ -839,7 +861,7 @@ private: void reset(); void copyFrom(const TouchState& other); void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds); - void removeOutsideTouchWindows(); + void filterNonAsIsTouchWindows(); const InputWindow* getFirstForegroundWindow(); }; @@ -882,6 +904,9 @@ private: bool mInputTargetWaitTimeoutExpired; sp<InputApplicationHandle> mInputTargetWaitApplication; + // Contains the last window which received a hover event. + const InputWindow* mLastHoverWindow; + // Finding targets for input events. void resetTargetsLocked(); void commitTargetsLocked(); @@ -896,7 +921,8 @@ private: int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, nsecs_t* nextWakeupTime); int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, - nsecs_t* nextWakeupTime, bool* outConflictingPointerActions); + nsecs_t* nextWakeupTime, bool* outConflictingPointerActions, + const MotionSample** outSplitBatchAfterSample); void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds); @@ -915,6 +941,9 @@ private: void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, bool resumeWithAppendedMotionSample); + void enqueueDispatchEntryLocked(const sp<Connection>& connection, + EventEntry* eventEntry, const InputTarget* inputTarget, + bool resumeWithAppendedMotionSample, int32_t dispatchMode); void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool handled); diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 3029028e4e76..82cf62f6f237 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -33,6 +33,9 @@ // Log debug messages about pointer assignment calculations. #define DEBUG_POINTER_ASSIGNMENT 0 +// Log debug messages about gesture detection. +#define DEBUG_GESTURES 0 + #include "InputReader.h" @@ -54,6 +57,38 @@ namespace android { +// --- Constants --- + +// Quiet time between certain gesture transitions. +// Time to allow for all fingers or buttons to settle into a stable state before +// starting a new gesture. +static const nsecs_t QUIET_INTERVAL = 100 * 1000000; // 100 ms + +// The minimum speed that a pointer must travel for us to consider switching the active +// touch pointer to it during a drag. This threshold is set to avoid switching due +// to noise from a finger resting on the touch pad (perhaps just pressing it down). +static const float DRAG_MIN_SWITCH_SPEED = 50.0f; // pixels per second + +// Tap gesture delay time. +// The time between down and up must be less than this to be considered a tap. +static const nsecs_t TAP_INTERVAL = 100 * 1000000; // 100 ms + +// The distance in pixels that the pointer is allowed to move from initial down +// to up and still be called a tap. +static const float TAP_SLOP = 5.0f; // 5 pixels + +// The transition from INDETERMINATE_MULTITOUCH to SWIPE or FREEFORM gesture mode is made when +// all of the pointers have traveled this number of pixels from the start point. +static const float MULTITOUCH_MIN_TRAVEL = 5.0f; + +// The transition from INDETERMINATE_MULTITOUCH to SWIPE gesture mode can only occur when the +// cosine of the angle between the two vectors is greater than or equal to than this value +// which indicates that the vectors are oriented in the same direction. +// When the vectors are oriented in the exactly same direction, the cosine is 1.0. +// (In exactly opposite directions, the cosine is -1.0.) +static const float SWIPE_TRANSITION_ANGLE_COSINE = 0.5f; // cosine of 45 degrees + + // --- Static Functions --- template<typename T> @@ -81,6 +116,12 @@ inline static float pythag(float x, float y) { return sqrtf(x * x + y * y); } +inline static int32_t distanceSquared(int32_t x1, int32_t y1, int32_t x2, int32_t y2) { + int32_t dx = x1 - x2; + int32_t dy = y1 - y2; + return dx * dx + dy * dy; +} + inline static int32_t signExtendNybble(int32_t value) { return value >= 8 ? value - 16 : value; } @@ -190,7 +231,7 @@ InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputDispatcherInterface>& dispatcher) : mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), - mGlobalMetaState(0), mDisableVirtualKeysTimeout(-1) { + mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX) { configureExcludedDevices(); updateGlobalMetaState(); updateInputConfiguration(); @@ -203,35 +244,61 @@ InputReader::~InputReader() { } void InputReader::loopOnce() { - RawEvent rawEvent; - mEventHub->getEvent(& rawEvent); + int32_t timeoutMillis = -1; + if (mNextTimeout != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); + } + size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); + if (count) { + processEvents(mEventBuffer, count); + } + if (!count || timeoutMillis == 0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); #if DEBUG_RAW_EVENTS - LOGD("Input event: device=%d type=0x%x scancode=%d keycode=%d value=%d", - rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode, - rawEvent.value); + LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); #endif - - process(& rawEvent); + mNextTimeout = LLONG_MAX; + timeoutExpired(now); + } } -void InputReader::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EventHubInterface::DEVICE_ADDED: - addDevice(rawEvent->deviceId); - break; - - case EventHubInterface::DEVICE_REMOVED: - removeDevice(rawEvent->deviceId); - break; - - case EventHubInterface::FINISHED_DEVICE_SCAN: - handleConfigurationChanged(rawEvent->when); - break; - - default: - consumeEvent(rawEvent); - break; +void InputReader::processEvents(const RawEvent* rawEvents, size_t count) { + for (const RawEvent* rawEvent = rawEvents; count;) { + int32_t type = rawEvent->type; + size_t batchSize = 1; + if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { + int32_t deviceId = rawEvent->deviceId; + while (batchSize < count) { + if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT + || rawEvent[batchSize].deviceId != deviceId) { + break; + } + batchSize += 1; + } +#if DEBUG_RAW_EVENTS + LOGD("BatchSize: %d Count: %d", batchSize, count); +#endif + processEventsForDevice(deviceId, rawEvent, batchSize); + } else { + switch (rawEvent->type) { + case EventHubInterface::DEVICE_ADDED: + addDevice(rawEvent->deviceId); + break; + case EventHubInterface::DEVICE_REMOVED: + removeDevice(rawEvent->deviceId); + break; + case EventHubInterface::FINISHED_DEVICE_SCAN: + handleConfigurationChanged(rawEvent->when); + break; + default: + assert(false); // can't happen + break; + } + } + count -= batchSize; + rawEvent += batchSize; } } @@ -352,9 +419,8 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui return device; } -void InputReader::consumeEvent(const RawEvent* rawEvent) { - int32_t deviceId = rawEvent->deviceId; - +void InputReader::processEventsForDevice(int32_t deviceId, + const RawEvent* rawEvents, size_t count) { { // acquire device registry reader lock RWLock::AutoRLock _rl(mDeviceRegistryLock); @@ -370,7 +436,20 @@ void InputReader::consumeEvent(const RawEvent* rawEvent) { return; } - device->process(rawEvent); + device->process(rawEvents, count); + } // release device registry reader lock +} + +void InputReader::timeoutExpired(nsecs_t when) { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored()) { + device->timeoutExpired(when); + } + } } // release device registry reader lock } @@ -484,6 +563,12 @@ void InputReader::fadePointer() { } // release device registry reader lock } +void InputReader::requestTimeoutAtTime(nsecs_t when) { + if (when < mNextTimeout) { + mNextTimeout = when; + } +} + void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) { { // acquire state lock AutoMutex _l(mStateLock); @@ -713,11 +798,33 @@ void InputDevice::reset() { } } -void InputDevice::process(const RawEvent* rawEvent) { +void InputDevice::process(const RawEvent* rawEvents, size_t count) { + // Process all of the events in order for each mapper. + // We cannot simply ask each mapper to process them in bulk because mappers may + // have side-effects that must be interleaved. For example, joystick movement events and + // gamepad button presses are handled by different mappers but they should be dispatched + // in the order received. + size_t numMappers = mMappers.size(); + for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) { +#if DEBUG_RAW_EVENTS + LOGD("Input event: device=%d type=0x%04x scancode=0x%04x " + "keycode=0x%04x value=0x%04x flags=0x%08x", + rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode, + rawEvent->value, rawEvent->flags); +#endif + + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->process(rawEvent); + } + } +} + +void InputDevice::timeoutExpired(nsecs_t when) { size_t numMappers = mMappers.size(); for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; - mapper->process(rawEvent); + mapper->timeoutExpired(when); } } @@ -812,6 +919,9 @@ void InputMapper::configure() { void InputMapper::reset() { } +void InputMapper::timeoutExpired(nsecs_t when) { +} + int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { return AKEY_STATE_UNKNOWN; } @@ -1495,8 +1605,15 @@ void CursorInputMapper::sync(nsecs_t when) { motionEventAction, 0, metaState, motionEventEdgeFlags, 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime); - mAccumulator.clear(); + // Send hover move after UP to tell the application that the mouse is hovering now. + if (motionEventAction == AMOTION_EVENT_ACTION_UP + && mPointerController != NULL) { + getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime); + } + // Send scroll events. if (vscroll != 0 || hscroll != 0) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); @@ -1505,6 +1622,8 @@ void CursorInputMapper::sync(nsecs_t when) { AMOTION_EVENT_ACTION_SCROLL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime); } + + mAccumulator.clear(); } int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { @@ -1540,7 +1659,7 @@ TouchInputMapper::~TouchInputMapper() { } uint32_t TouchInputMapper::getSources() { - return mTouchSource; + return mTouchSource | mPointerSource; } void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { @@ -1579,6 +1698,18 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { if (mLocked.orientedRanges.haveOrientation) { info->addMotionRange(mLocked.orientedRanges.orientation); } + + if (mPointerController != NULL) { + float minX, minY, maxX, maxY; + if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mPointerSource, + minX, maxX, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mPointerSource, + minY, maxY, 0.0f, 0.0f); + } + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mPointerSource, + 0.0f, 1.0f, 0.0f, 0.0f); + } } // release lock } @@ -1608,6 +1739,21 @@ void TouchInputMapper::dump(String8& dump) { dump.appendFormat(INDENT3 "Last Touch:\n"); dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount); + dump.appendFormat(INDENT4 "Button State: 0x%08x\n", mLastTouch.buttonState); + + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n"); + dump.appendFormat(INDENT4 "XMovementScale: %0.3f\n", + mLocked.pointerGestureXMovementScale); + dump.appendFormat(INDENT4 "YMovementScale: %0.3f\n", + mLocked.pointerGestureYMovementScale); + dump.appendFormat(INDENT4 "XZoomScale: %0.3f\n", + mLocked.pointerGestureXZoomScale); + dump.appendFormat(INDENT4 "YZoomScale: %0.3f\n", + mLocked.pointerGestureYZoomScale); + dump.appendFormat(INDENT4 "MaxSwipeWidthSquared: %d\n", + mLocked.pointerGestureMaxSwipeWidthSquared); + } } // release lock } @@ -1630,6 +1776,8 @@ void TouchInputMapper::initializeLocked() { mLocked.orientedRanges.haveTouchSize = false; mLocked.orientedRanges.haveToolSize = false; mLocked.orientedRanges.haveOrientation = false; + + mPointerGesture.reset(); } void TouchInputMapper::configure() { @@ -1642,9 +1790,15 @@ void TouchInputMapper::configure() { switch (mParameters.deviceType) { case Parameters::DEVICE_TYPE_TOUCH_SCREEN: mTouchSource = AINPUT_SOURCE_TOUCHSCREEN; + mPointerSource = 0; break; case Parameters::DEVICE_TYPE_TOUCH_PAD: mTouchSource = AINPUT_SOURCE_TOUCHPAD; + mPointerSource = 0; + break; + case Parameters::DEVICE_TYPE_POINTER: + mTouchSource = AINPUT_SOURCE_TOUCHPAD; + mPointerSource = AINPUT_SOURCE_MOUSE; break; default: assert(false); @@ -1671,14 +1825,26 @@ void TouchInputMapper::configureParameters() { mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); mParameters.virtualKeyQuietTime = getPolicy()->getVirtualKeyQuietTime(); + if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) + || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { + // The device is a cursor device with a touch pad attached. + // By default don't use the touch pad to move the pointer. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else { + // The device is just a touch pad. + // By default use the touch pad to move the pointer and to perform related gestures. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } + String8 deviceTypeString; - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), deviceTypeString)) { if (deviceTypeString == "touchScreen") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; } else if (deviceTypeString == "touchPad") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (deviceTypeString == "pointer") { + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } else { LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); } @@ -1690,6 +1856,7 @@ void TouchInputMapper::configureParameters() { mParameters.associatedDisplayId = mParameters.orientationAware || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN + || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER ? 0 : -1; } @@ -1703,6 +1870,9 @@ void TouchInputMapper::dumpParameters(String8& dump) { case Parameters::DEVICE_TYPE_TOUCH_PAD: dump.append(INDENT4 "DeviceType: touchPad\n"); break; + case Parameters::DEVICE_TYPE_POINTER: + dump.append(INDENT4 "DeviceType: pointer\n"); + break; default: assert(false); } @@ -1776,6 +1946,11 @@ bool TouchInputMapper::configureSurfaceLocked() { } } + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER + && mPointerController == NULL) { + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + } + bool orientationChanged = mLocked.surfaceOrientation != orientation; if (orientationChanged) { mLocked.surfaceOrientation = orientation; @@ -1997,6 +2172,37 @@ bool TouchInputMapper::configureSurfaceLocked() { mLocked.orientedRanges.y.fuzz = mLocked.yScale; break; } + + // Compute pointer gesture detection parameters. + // TODO: These factors should not be hardcoded. + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + int32_t rawWidth = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1; + int32_t rawHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1; + + // Scale movements such that one whole swipe of the touch pad covers a portion + // of the display along whichever axis of the touch pad is longer. + // Assume that the touch pad has a square aspect ratio such that movements in + // X and Y of the same number of raw units cover the same physical distance. + const float scaleFactor = 0.8f; + + mLocked.pointerGestureXMovementScale = rawWidth > rawHeight + ? scaleFactor * float(mLocked.associatedDisplayWidth) / rawWidth + : scaleFactor * float(mLocked.associatedDisplayHeight) / rawHeight; + mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale; + + // Scale zooms to cover a smaller range of the display than movements do. + // This value determines the area around the pointer that is affected by freeform + // pointer gestures. + mLocked.pointerGestureXZoomScale = mLocked.pointerGestureXMovementScale * 0.4f; + mLocked.pointerGestureYZoomScale = mLocked.pointerGestureYMovementScale * 0.4f; + + // Max width between pointers to detect a swipe gesture is 3/4 of the short + // axis of the touch pad. Touches that are wider than this are translated + // into freeform gestures. + mLocked.pointerGestureMaxSwipeWidthSquared = min(rawWidth, rawHeight) * 3 / 4; + mLocked.pointerGestureMaxSwipeWidthSquared *= + mLocked.pointerGestureMaxSwipeWidthSquared; + } } return true; @@ -2428,6 +2634,19 @@ void TouchInputMapper::reset() { } void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { +#if DEBUG_RAW_EVENTS + if (!havePointerIds) { + LOGD("syncTouch: pointerCount=%d, no pointer ids", mCurrentTouch.pointerCount); + } else { + LOGD("syncTouch: pointerCount=%d, up=0x%08x, down=0x%08x, move=0x%08x, " + "last=0x%08x, current=0x%08x", mCurrentTouch.pointerCount, + mLastTouch.idBits.value & ~mCurrentTouch.idBits.value, + mCurrentTouch.idBits.value & ~mLastTouch.idBits.value, + mLastTouch.idBits.value & mCurrentTouch.idBits.value, + mLastTouch.idBits.value, mCurrentTouch.idBits.value); + } +#endif + // Preprocess pointer data. if (mParameters.useBadTouchFilter) { if (applyBadTouchFilter()) { @@ -2441,7 +2660,7 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { } } - if (! havePointerIds) { + if (!havePointerIds) { calculatePointerIds(); } @@ -2476,12 +2695,17 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { TouchResult touchResult = consumeOffScreenTouches(when, policyFlags); if (touchResult == DISPATCH_TOUCH) { suppressSwipeOntoVirtualKeys(when); + if (mPointerController != NULL) { + dispatchPointerGestures(when, policyFlags); + } dispatchTouches(when, policyFlags); } // Copy current touch to last touch in preparation for the next cycle. + // Keep the button state so we can track edge-triggered button state changes. if (touchResult == DROP_STROKE) { mLastTouch.clear(); + mLastTouch.buttonState = savedTouch->buttonState; } else { mLastTouch.copyFrom(*savedTouch); } @@ -2629,329 +2853,1097 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { return; // nothing to do! } + // Update current touch coordinates. + int32_t edgeFlags; + float xPrecision, yPrecision; + prepareTouches(&edgeFlags, &xPrecision, &yPrecision); + + // Dispatch motions. BitSet32 currentIdBits = mCurrentTouch.idBits; BitSet32 lastIdBits = mLastTouch.idBits; + uint32_t metaState = getContext()->getGlobalMetaState(); if (currentIdBits == lastIdBits) { // No pointer id changes so this is a move event. // The dispatcher takes care of batching moves so we don't have to deal with that here. - int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE; - dispatchTouch(when, policyFlags, & mCurrentTouch, - currentIdBits, -1, currentPointerCount, motionEventAction); + dispatchMotion(when, policyFlags, mTouchSource, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentTouchCoords, mCurrentTouch.idToIndex, currentIdBits, -1, + xPrecision, yPrecision, mDownTime); } else { // There may be pointers going up and pointers going down and pointers moving // all at the same time. - BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value); - BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value); - BitSet32 activeIdBits(lastIdBits.value); - uint32_t pointerCount = lastPointerCount; - - // Produce an intermediate representation of the touch data that consists of the - // old location of pointers that have just gone up and the new location of pointers that - // have just moved but omits the location of pointers that have just gone down. - TouchData interimTouch; - interimTouch.copyFrom(mLastTouch); - + BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value); + BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value); BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value); - bool moveNeeded = false; - while (!moveIdBits.isEmpty()) { - uint32_t moveId = moveIdBits.firstMarkedBit(); - moveIdBits.clearBit(moveId); - - int32_t oldIndex = mLastTouch.idToIndex[moveId]; - int32_t newIndex = mCurrentTouch.idToIndex[moveId]; - if (mLastTouch.pointers[oldIndex] != mCurrentTouch.pointers[newIndex]) { - interimTouch.pointers[oldIndex] = mCurrentTouch.pointers[newIndex]; - moveNeeded = true; - } - } + BitSet32 dispatchedIdBits(lastIdBits.value); + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool moveNeeded = updateMovedPointerCoords( + mCurrentTouchCoords, mCurrentTouch.idToIndex, + mLastTouchCoords, mLastTouch.idToIndex, + moveIdBits); - // Dispatch pointer up events using the interim pointer locations. + // Dispatch pointer up events. while (!upIdBits.isEmpty()) { uint32_t upId = upIdBits.firstMarkedBit(); upIdBits.clearBit(upId); - BitSet32 oldActiveIdBits = activeIdBits; - activeIdBits.clearBit(upId); - int32_t motionEventAction; - if (activeIdBits.isEmpty()) { - motionEventAction = AMOTION_EVENT_ACTION_UP; - } else { - motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP; - } - - dispatchTouch(when, policyFlags, &interimTouch, - oldActiveIdBits, upId, pointerCount, motionEventAction); - pointerCount -= 1; + dispatchMotion(when, policyFlags, mTouchSource, + AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, 0, + mLastTouchCoords, mLastTouch.idToIndex, dispatchedIdBits, upId, + xPrecision, yPrecision, mDownTime); + dispatchedIdBits.clearBit(upId); } // Dispatch move events if any of the remaining pointers moved from their old locations. // Although applications receive new locations as part of individual pointer up // events, they do not generally handle them except when presented in a move event. if (moveNeeded) { - dispatchTouch(when, policyFlags, &mCurrentTouch, - activeIdBits, -1, pointerCount, AMOTION_EVENT_ACTION_MOVE); + assert(moveIdBits.value == dispatchedIdBits.value); + dispatchMotion(when, policyFlags, mTouchSource, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, 0, + mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, -1, + xPrecision, yPrecision, mDownTime); } // Dispatch pointer down events using the new pointer locations. while (!downIdBits.isEmpty()) { uint32_t downId = downIdBits.firstMarkedBit(); downIdBits.clearBit(downId); - BitSet32 oldActiveIdBits = activeIdBits; - activeIdBits.markBit(downId); + dispatchedIdBits.markBit(downId); - int32_t motionEventAction; - if (oldActiveIdBits.isEmpty()) { - motionEventAction = AMOTION_EVENT_ACTION_DOWN; + if (dispatchedIdBits.count() == 1) { + // First pointer is going down. Set down time. mDownTime = when; } else { - motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN; + // Only send edge flags with first pointer down. + edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; } - pointerCount += 1; - dispatchTouch(when, policyFlags, &mCurrentTouch, - activeIdBits, downId, pointerCount, motionEventAction); + dispatchMotion(when, policyFlags, mTouchSource, + AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, edgeFlags, + mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, downId, + xPrecision, yPrecision, mDownTime); } } -} -void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, - TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, - int32_t motionEventAction) { - int32_t pointerIds[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - int32_t motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; - float xPrecision, yPrecision; + // Update state for next time. + for (uint32_t i = 0; i < currentPointerCount; i++) { + mLastTouchCoords[i].copyFrom(mCurrentTouchCoords[i]); + } +} - { // acquire lock - AutoMutex _l(mLock); +void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags, + float* outXPrecision, float* outYPrecision) { + uint32_t currentPointerCount = mCurrentTouch.pointerCount; + uint32_t lastPointerCount = mLastTouch.pointerCount; - // Walk through the the active pointers and map touch screen coordinates (TouchData) into - // display or surface coordinates (PointerCoords) and adjust for display orientation. - for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - uint32_t inIndex = touch->idToIndex[id]; + AutoMutex _l(mLock); - const PointerData& in = touch->pointers[inIndex]; + // Walk through the the active pointers and map touch screen coordinates (TouchData) into + // display or surface coordinates (PointerCoords) and adjust for display orientation. + for (uint32_t i = 0; i < currentPointerCount; i++) { + const PointerData& in = mCurrentTouch.pointers[i]; - // ToolMajor and ToolMinor - float toolMajor, toolMinor; - switch (mCalibration.toolSizeCalibration) { - case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC: - toolMajor = in.toolMajor * mLocked.geometricScale; - if (mRawAxes.toolMinor.valid) { - toolMinor = in.toolMinor * mLocked.geometricScale; - } else { - toolMinor = toolMajor; - } - break; - case Calibration::TOOL_SIZE_CALIBRATION_LINEAR: - toolMajor = in.toolMajor != 0 - ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias + // ToolMajor and ToolMinor + float toolMajor, toolMinor; + switch (mCalibration.toolSizeCalibration) { + case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC: + toolMajor = in.toolMajor * mLocked.geometricScale; + if (mRawAxes.toolMinor.valid) { + toolMinor = in.toolMinor * mLocked.geometricScale; + } else { + toolMinor = toolMajor; + } + break; + case Calibration::TOOL_SIZE_CALIBRATION_LINEAR: + toolMajor = in.toolMajor != 0 + ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias + : 0; + if (mRawAxes.toolMinor.valid) { + toolMinor = in.toolMinor != 0 + ? in.toolMinor * mLocked.toolSizeLinearScale + + mLocked.toolSizeLinearBias : 0; - if (mRawAxes.toolMinor.valid) { - toolMinor = in.toolMinor != 0 - ? in.toolMinor * mLocked.toolSizeLinearScale - + mLocked.toolSizeLinearBias - : 0; - } else { - toolMinor = toolMajor; - } - break; - case Calibration::TOOL_SIZE_CALIBRATION_AREA: - if (in.toolMajor != 0) { - float diameter = sqrtf(in.toolMajor - * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias); - toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias; - } else { - toolMajor = 0; - } + } else { toolMinor = toolMajor; - break; - default: + } + break; + case Calibration::TOOL_SIZE_CALIBRATION_AREA: + if (in.toolMajor != 0) { + float diameter = sqrtf(in.toolMajor + * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias); + toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias; + } else { toolMajor = 0; - toolMinor = 0; - break; } + toolMinor = toolMajor; + break; + default: + toolMajor = 0; + toolMinor = 0; + break; + } + + if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) { + toolMajor /= currentPointerCount; + toolMinor /= currentPointerCount; + } - if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) { - toolMajor /= pointerCount; - toolMinor /= pointerCount; + // Pressure + float rawPressure; + switch (mCalibration.pressureSource) { + case Calibration::PRESSURE_SOURCE_PRESSURE: + rawPressure = in.pressure; + break; + case Calibration::PRESSURE_SOURCE_TOUCH: + rawPressure = in.touchMajor; + break; + default: + rawPressure = 0; + } + + float pressure; + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + pressure = rawPressure * mLocked.pressureScale; + break; + default: + pressure = 1; + break; + } + + // TouchMajor and TouchMinor + float touchMajor, touchMinor; + switch (mCalibration.touchSizeCalibration) { + case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC: + touchMajor = in.touchMajor * mLocked.geometricScale; + if (mRawAxes.touchMinor.valid) { + touchMinor = in.touchMinor * mLocked.geometricScale; + } else { + touchMinor = touchMajor; } + break; + case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE: + touchMajor = toolMajor * pressure; + touchMinor = toolMinor * pressure; + break; + default: + touchMajor = 0; + touchMinor = 0; + break; + } - // Pressure - float rawPressure; - switch (mCalibration.pressureSource) { - case Calibration::PRESSURE_SOURCE_PRESSURE: - rawPressure = in.pressure; - break; - case Calibration::PRESSURE_SOURCE_TOUCH: - rawPressure = in.touchMajor; - break; - default: - rawPressure = 0; + if (touchMajor > toolMajor) { + touchMajor = toolMajor; + } + if (touchMinor > toolMinor) { + touchMinor = toolMinor; + } + + // Size + float size; + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_NORMALIZED: { + float rawSize = mRawAxes.toolMinor.valid + ? avg(in.toolMajor, in.toolMinor) + : in.toolMajor; + size = rawSize * mLocked.sizeScale; + break; + } + default: + size = 0; + break; + } + + // Orientation + float orientation; + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + orientation = in.orientation * mLocked.orientationScale; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: { + int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); + int32_t c2 = signExtendNybble(in.orientation & 0x0f); + if (c1 != 0 || c2 != 0) { + orientation = atan2f(c1, c2) * 0.5f; + float scale = 1.0f + pythag(c1, c2) / 16.0f; + touchMajor *= scale; + touchMinor /= scale; + toolMajor *= scale; + toolMinor /= scale; + } else { + orientation = 0; } + break; + } + default: + orientation = 0; + } - float pressure; - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - pressure = rawPressure * mLocked.pressureScale; - break; - default: - pressure = 1; - break; + // X and Y + // Adjust coords for surface orientation. + float x, y; + switch (mLocked.surfaceOrientation) { + case DISPLAY_ORIENTATION_90: + x = float(in.y - mRawAxes.y.minValue) * mLocked.yScale; + y = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale; + orientation -= M_PI_2; + if (orientation < - M_PI_2) { + orientation += M_PI; + } + break; + case DISPLAY_ORIENTATION_180: + x = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale; + y = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale; + break; + case DISPLAY_ORIENTATION_270: + x = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale; + y = float(in.x - mRawAxes.x.minValue) * mLocked.xScale; + orientation += M_PI_2; + if (orientation > M_PI_2) { + orientation -= M_PI; } + break; + default: + x = float(in.x - mRawAxes.x.minValue) * mLocked.xScale; + y = float(in.y - mRawAxes.y.minValue) * mLocked.yScale; + break; + } - // TouchMajor and TouchMinor - float touchMajor, touchMinor; - switch (mCalibration.touchSizeCalibration) { - case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC: - touchMajor = in.touchMajor * mLocked.geometricScale; - if (mRawAxes.touchMinor.valid) { - touchMinor = in.touchMinor * mLocked.geometricScale; - } else { - touchMinor = touchMajor; - } - break; - case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE: - touchMajor = toolMajor * pressure; - touchMinor = toolMinor * pressure; - break; - default: - touchMajor = 0; - touchMinor = 0; - break; + // Write output coords. + PointerCoords& out = mCurrentTouchCoords[i]; + out.clear(); + out.setAxisValue(AMOTION_EVENT_AXIS_X, x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); + } + + // Check edge flags by looking only at the first pointer since the flags are + // global to the event. + *outEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; + if (lastPointerCount == 0 && currentPointerCount > 0) { + const PointerData& in = mCurrentTouch.pointers[0]; + + if (in.x <= mRawAxes.x.minValue) { + *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT, + mLocked.surfaceOrientation); + } else if (in.x >= mRawAxes.x.maxValue) { + *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT, + mLocked.surfaceOrientation); + } + if (in.y <= mRawAxes.y.minValue) { + *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP, + mLocked.surfaceOrientation); + } else if (in.y >= mRawAxes.y.maxValue) { + *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM, + mLocked.surfaceOrientation); + } + } + + *outXPrecision = mLocked.orientedXPrecision; + *outYPrecision = mLocked.orientedYPrecision; +} + +void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags) { + // Update current gesture coordinates. + bool cancelPreviousGesture, finishPreviousGesture; + preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture); + + // Send events! + uint32_t metaState = getContext()->getGlobalMetaState(); + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool down = mPointerGesture.currentGestureMode == PointerGesture::CLICK_OR_DRAG + || mPointerGesture.currentGestureMode == PointerGesture::SWIPE + || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; + bool moveNeeded = false; + if (down && !cancelPreviousGesture && !finishPreviousGesture + && mPointerGesture.lastGesturePointerCount != 0 + && mPointerGesture.currentGesturePointerCount != 0) { + BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value + & mPointerGesture.lastGestureIdBits.value); + moveNeeded = updateMovedPointerCoords( + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, + movedGestureIdBits); + } + + // Send motion events for all pointers that went up or were canceled. + BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); + if (!dispatchedGestureIdBits.isEmpty()) { + if (cancelPreviousGesture) { + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_CANCEL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, + dispatchedGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + + dispatchedGestureIdBits.clear(); + } else { + BitSet32 upGestureIdBits; + if (finishPreviousGesture) { + upGestureIdBits = dispatchedGestureIdBits; + } else { + upGestureIdBits.value = dispatchedGestureIdBits.value + & ~mPointerGesture.currentGestureIdBits.value; } + while (!upGestureIdBits.isEmpty()) { + uint32_t id = upGestureIdBits.firstMarkedBit(); + upGestureIdBits.clearBit(id); + + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_POINTER_UP, 0, + metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, + dispatchedGestureIdBits, id, + 0, 0, mPointerGesture.downTime); + + dispatchedGestureIdBits.clearBit(id); + } + } + } + + // Send motion events for all pointers that moved. + if (moveNeeded) { + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + dispatchedGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + } + + // Send motion events for all pointers that went down. + if (down) { + BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value + & ~dispatchedGestureIdBits.value); + while (!downGestureIdBits.isEmpty()) { + uint32_t id = downGestureIdBits.firstMarkedBit(); + downGestureIdBits.clearBit(id); + dispatchedGestureIdBits.markBit(id); + + int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE; + if (dispatchedGestureIdBits.count() == 1) { + // First pointer is going down. Calculate edge flags and set down time. + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerCoords& downCoords = mPointerGesture.currentGestureCoords[index]; + edgeFlags = calculateEdgeFlagsUsingPointerBounds(mPointerController, + downCoords.getAxisValue(AMOTION_EVENT_AXIS_X), + downCoords.getAxisValue(AMOTION_EVENT_AXIS_Y)); + mPointerGesture.downTime = when; + } + + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, edgeFlags, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + dispatchedGestureIdBits, id, + 0, 0, mPointerGesture.downTime); + } + } - if (touchMajor > toolMajor) { - touchMajor = toolMajor; + // Send down and up for a tap. + if (mPointerGesture.currentGestureMode == PointerGesture::TAP) { + const PointerCoords& coords = mPointerGesture.currentGestureCoords[0]; + int32_t edgeFlags = calculateEdgeFlagsUsingPointerBounds(mPointerController, + coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y)); + nsecs_t downTime = mPointerGesture.downTime = mPointerGesture.tapTime; + mPointerGesture.resetTapTime(); + + dispatchMotion(downTime, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_DOWN, 0, metaState, edgeFlags, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, + 0, 0, downTime); + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_UP, 0, metaState, edgeFlags, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, + 0, 0, downTime); + } + + // Send motion events for hover. + if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { + dispatchMotion(when, policyFlags, mPointerSource, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + } + + // Update state. + mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; + if (!down) { + mPointerGesture.lastGesturePointerCount = 0; + mPointerGesture.lastGestureIdBits.clear(); + } else { + uint32_t currentGesturePointerCount = mPointerGesture.currentGesturePointerCount; + mPointerGesture.lastGesturePointerCount = currentGesturePointerCount; + mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; + for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + mPointerGesture.lastGestureCoords[index].copyFrom( + mPointerGesture.currentGestureCoords[index]); + mPointerGesture.lastGestureIdToIndex[id] = index; + } + } +} + +void TouchInputMapper::preparePointerGestures(nsecs_t when, + bool* outCancelPreviousGesture, bool* outFinishPreviousGesture) { + *outCancelPreviousGesture = false; + *outFinishPreviousGesture = false; + + AutoMutex _l(mLock); + + // Update the velocity tracker. + { + VelocityTracker::Position positions[MAX_POINTERS]; + uint32_t count = 0; + for (BitSet32 idBits(mCurrentTouch.idBits); !idBits.isEmpty(); count++) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = mCurrentTouch.idToIndex[id]; + positions[count].x = mCurrentTouch.pointers[index].x + * mLocked.pointerGestureXMovementScale; + positions[count].y = mCurrentTouch.pointers[index].y + * mLocked.pointerGestureYMovementScale; + } + mPointerGesture.velocityTracker.addMovement(when, mCurrentTouch.idBits, positions); + } + + // Pick a new active touch id if needed. + // Choose an arbitrary pointer that just went down, if there is one. + // Otherwise choose an arbitrary remaining pointer. + // This guarantees we always have an active touch id when there is at least one pointer. + // We always switch to the newest pointer down because that's usually where the user's + // attention is focused. + int32_t activeTouchId; + BitSet32 downTouchIdBits(mCurrentTouch.idBits.value & ~mLastTouch.idBits.value); + if (!downTouchIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = downTouchIdBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId; + if (activeTouchId < 0 || !mCurrentTouch.idBits.hasBit(activeTouchId)) { + if (!mCurrentTouch.idBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentTouch.idBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId = -1; } - if (touchMinor > toolMinor) { - touchMinor = toolMinor; + } + } + + // Update the touch origin data to track where each finger originally went down. + if (mCurrentTouch.pointerCount == 0 || mPointerGesture.touchOrigin.pointerCount == 0) { + // Fast path when all fingers have gone up or down. + mPointerGesture.touchOrigin.copyFrom(mCurrentTouch); + } else { + // Slow path when only some fingers have gone up or down. + for (BitSet32 idBits(mPointerGesture.touchOrigin.idBits.value + & ~mCurrentTouch.idBits.value); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + mPointerGesture.touchOrigin.idBits.clearBit(id); + uint32_t index = mPointerGesture.touchOrigin.idToIndex[id]; + uint32_t count = --mPointerGesture.touchOrigin.pointerCount; + while (index < count) { + mPointerGesture.touchOrigin.pointers[index] = + mPointerGesture.touchOrigin.pointers[index + 1]; + uint32_t movedId = mPointerGesture.touchOrigin.pointers[index].id; + mPointerGesture.touchOrigin.idToIndex[movedId] = index; + index += 1; } + } + for (BitSet32 idBits(mCurrentTouch.idBits.value + & ~mPointerGesture.touchOrigin.idBits.value); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + mPointerGesture.touchOrigin.idBits.markBit(id); + uint32_t index = mPointerGesture.touchOrigin.pointerCount++; + mPointerGesture.touchOrigin.pointers[index] = + mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]]; + mPointerGesture.touchOrigin.idToIndex[id] = index; + } + } - // Size - float size; - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NORMALIZED: { - float rawSize = mRawAxes.toolMinor.valid - ? avg(in.toolMajor, in.toolMinor) - : in.toolMajor; - size = rawSize * mLocked.sizeScale; - break; + // Determine whether we are in quiet time. + bool isQuietTime = when < mPointerGesture.quietTime + QUIET_INTERVAL; + if (!isQuietTime) { + if ((mPointerGesture.lastGestureMode == PointerGesture::SWIPE + || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) + && mCurrentTouch.pointerCount < 2) { + // Enter quiet time when exiting swipe or freeform state. + // This is to prevent accidentally entering the hover state and flinging the + // pointer when finishing a swipe and there is still one pointer left onscreen. + isQuietTime = true; + } else if (mPointerGesture.lastGestureMode == PointerGesture::CLICK_OR_DRAG + && mCurrentTouch.pointerCount >= 2 + && !isPointerDown(mCurrentTouch.buttonState)) { + // Enter quiet time when releasing the button and there are still two or more + // fingers down. This may indicate that one finger was used to press the button + // but it has not gone up yet. + isQuietTime = true; + } + if (isQuietTime) { + mPointerGesture.quietTime = when; + } + } + + // Switch states based on button and pointer state. + if (isQuietTime) { + // Case 1: Quiet time. (QUIET) +#if DEBUG_GESTURES + LOGD("Gestures: QUIET for next %0.3fms", + (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f); +#endif + *outFinishPreviousGesture = true; + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::QUIET; + mPointerGesture.currentGesturePointerCount = 0; + mPointerGesture.currentGestureIdBits.clear(); + } else if (isPointerDown(mCurrentTouch.buttonState)) { + // Case 2: Button is pressed. (DRAG) + // The pointer follows the active touch point. + // Emit DOWN, MOVE, UP events at the pointer location. + // + // Only the active touch matters; other fingers are ignored. This policy helps + // to handle the case where the user places a second finger on the touch pad + // to apply the necessary force to depress an integrated button below the surface. + // We don't want the second finger to be delivered to applications. + // + // For this to work well, we need to make sure to track the pointer that is really + // active. If the user first puts one finger down to click then adds another + // finger to drag then the active pointer should switch to the finger that is + // being dragged. +#if DEBUG_GESTURES + LOGD("Gestures: CLICK_OR_DRAG activeTouchId=%d, " + "currentTouchPointerCount=%d", activeTouchId, mCurrentTouch.pointerCount); +#endif + // Reset state when just starting. + if (mPointerGesture.lastGestureMode != PointerGesture::CLICK_OR_DRAG) { + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = 0; + } + + // Switch pointers if needed. + // Find the fastest pointer and follow it. + if (activeTouchId >= 0) { + if (mCurrentTouch.pointerCount > 1) { + int32_t bestId = -1; + float bestSpeed = DRAG_MIN_SWITCH_SPEED; + for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { + uint32_t id = mCurrentTouch.pointers[i].id; + float vx, vy; + if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { + float speed = pythag(vx, vy); + if (speed > bestSpeed) { + bestId = id; + bestSpeed = speed; + } + } + } + if (bestId >= 0 && bestId != activeTouchId) { + mPointerGesture.activeTouchId = activeTouchId = bestId; +#if DEBUG_GESTURES + LOGD("Gestures: CLICK_OR_DRAG switched pointers, " + "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed); +#endif + } } - default: - size = 0; - break; + + if (mLastTouch.idBits.hasBit(activeTouchId)) { + const PointerData& currentPointer = + mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]]; + const PointerData& lastPointer = + mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]]; + float deltaX = (currentPointer.x - lastPointer.x) + * mLocked.pointerGestureXMovementScale; + float deltaY = (currentPointer.y - lastPointer.y) + * mLocked.pointerGestureYMovementScale; + mPointerController->move(deltaX, deltaY); } + } - // Orientation - float orientation; - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - orientation = in.orientation * mLocked.orientationScale; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: { - int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); - int32_t c2 = signExtendNybble(in.orientation & 0x0f); - if (c1 != 0 || c2 != 0) { - orientation = atan2f(c1, c2) * 0.5f; - float scale = 1.0f + pythag(c1, c2) / 16.0f; - touchMajor *= scale; - touchMinor /= scale; - toolMajor *= scale; - toolMinor /= scale; + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureMode = PointerGesture::CLICK_OR_DRAG; + mPointerGesture.currentGesturePointerCount = 1; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (mCurrentTouch.pointerCount == 0) { + // Case 3. No fingers down and button is not pressed. (NEUTRAL) + *outFinishPreviousGesture = true; + + // Watch for taps coming out of HOVER or INDETERMINATE_MULTITOUCH mode. + bool tapped = false; + if (mPointerGesture.lastGestureMode == PointerGesture::HOVER + || mPointerGesture.lastGestureMode + == PointerGesture::INDETERMINATE_MULTITOUCH) { + if (when <= mPointerGesture.tapTime + TAP_INTERVAL) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.initialPointerX) <= TAP_SLOP + && fabs(y - mPointerGesture.initialPointerY) <= TAP_SLOP) { +#if DEBUG_GESTURES + LOGD("Gestures: TAP"); +#endif + mPointerGesture.activeGestureId = 0; + mPointerGesture.currentGestureMode = PointerGesture::TAP; + mPointerGesture.currentGesturePointerCount = 1; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit( + mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[ + mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue( + AMOTION_EVENT_AXIS_X, mPointerGesture.initialPointerX); + mPointerGesture.currentGestureCoords[0].setAxisValue( + AMOTION_EVENT_AXIS_Y, mPointerGesture.initialPointerY); + mPointerGesture.currentGestureCoords[0].setAxisValue( + AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + tapped = true; } else { - orientation = 0; +#if DEBUG_GESTURES + LOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", + x - mPointerGesture.initialPointerX, + y - mPointerGesture.initialPointerY); +#endif } - break; + } else { +#if DEBUG_GESTURES + LOGD("Gestures: Not a TAP, delay=%lld", + when - mPointerGesture.tapTime); +#endif } - default: - orientation = 0; + } + if (!tapped) { +#if DEBUG_GESTURES + LOGD("Gestures: NEUTRAL"); +#endif + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGesturePointerCount = 0; + mPointerGesture.currentGestureIdBits.clear(); + } + } else if (mCurrentTouch.pointerCount == 1) { + // Case 4. Exactly one finger down, button is not pressed. (HOVER) + // The pointer follows the active touch point. + // Emit HOVER_MOVE events at the pointer location. + assert(activeTouchId >= 0); + +#if DEBUG_GESTURES + LOGD("Gestures: HOVER"); +#endif + + if (mLastTouch.idBits.hasBit(activeTouchId)) { + const PointerData& currentPointer = + mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]]; + const PointerData& lastPointer = + mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]]; + float deltaX = (currentPointer.x - lastPointer.x) + * mLocked.pointerGestureXMovementScale; + float deltaY = (currentPointer.y - lastPointer.y) + * mLocked.pointerGestureYMovementScale; + mPointerController->move(deltaX, deltaY); + } + + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = 0; + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureMode = PointerGesture::HOVER; + mPointerGesture.currentGesturePointerCount = 1; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); + + if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) { + mPointerGesture.tapTime = when; + mPointerGesture.initialPointerX = x; + mPointerGesture.initialPointerY = y; + } + } else { + // Case 5. At least two fingers down, button is not pressed. (SWIPE or FREEFORM + // or INDETERMINATE_MULTITOUCH) + // Initially we watch and wait for something interesting to happen so as to + // avoid making a spurious guess as to the nature of the gesture. For example, + // the fingers may be in transition to some other state such as pressing or + // releasing the button or we may be performing a two finger tap. + // + // Fix the centroid of the figure when the gesture actually starts. + // We do not recalculate the centroid at any other time during the gesture because + // it would affect the relationship of the touch points relative to the pointer location. + assert(activeTouchId >= 0); + + uint32_t currentTouchPointerCount = mCurrentTouch.pointerCount; + if (currentTouchPointerCount > MAX_POINTERS) { + currentTouchPointerCount = MAX_POINTERS; + } + + if (mPointerGesture.lastGestureMode != PointerGesture::INDETERMINATE_MULTITOUCH + && mPointerGesture.lastGestureMode != PointerGesture::SWIPE + && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + mPointerGesture.currentGestureMode = PointerGesture::INDETERMINATE_MULTITOUCH; + + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = -1; + + // Remember the initial pointer location. + // Everything we do will be relative to this location. + mPointerController->getPosition(&mPointerGesture.initialPointerX, + &mPointerGesture.initialPointerY); + + // Track taps. + if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) { + mPointerGesture.tapTime = when; } - // X and Y - // Adjust coords for surface orientation. - float x, y; - switch (mLocked.surfaceOrientation) { - case DISPLAY_ORIENTATION_90: - x = float(in.y - mRawAxes.y.minValue) * mLocked.yScale; - y = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale; - orientation -= M_PI_2; - if (orientation < - M_PI_2) { - orientation += M_PI; - } - break; - case DISPLAY_ORIENTATION_180: - x = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale; - y = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale; - break; - case DISPLAY_ORIENTATION_270: - x = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale; - y = float(in.x - mRawAxes.x.minValue) * mLocked.xScale; - orientation += M_PI_2; - if (orientation > M_PI_2) { - orientation -= M_PI; + // Reset the touch origin to be relative to exactly where the fingers are now + // in case they have moved some distance away as part of a previous gesture. + // We want to know how far the fingers have traveled since we started considering + // a multitouch gesture. + mPointerGesture.touchOrigin.copyFrom(mCurrentTouch); + } else { + mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; + } + + if (mPointerGesture.currentGestureMode == PointerGesture::INDETERMINATE_MULTITOUCH) { + // Wait for the pointers to start moving before doing anything. + bool decideNow = true; + for (uint32_t i = 0; i < currentTouchPointerCount; i++) { + const PointerData& current = mCurrentTouch.pointers[i]; + const PointerData& origin = mPointerGesture.touchOrigin.pointers[ + mPointerGesture.touchOrigin.idToIndex[current.id]]; + float distance = pythag( + (current.x - origin.x) * mLocked.pointerGestureXZoomScale, + (current.y - origin.y) * mLocked.pointerGestureYZoomScale); + if (distance < MULTITOUCH_MIN_TRAVEL) { + decideNow = false; + break; } - break; - default: - x = float(in.x - mRawAxes.x.minValue) * mLocked.xScale; - y = float(in.y - mRawAxes.y.minValue) * mLocked.yScale; - break; } - // Write output coords. - PointerCoords& out = pointerCoords[outIndex]; - out.clear(); - out.setAxisValue(AMOTION_EVENT_AXIS_X, x); - out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); - out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); - - pointerIds[outIndex] = int32_t(id); - - if (id == changedId) { - motionEventAction |= outIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + if (decideNow) { + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + if (currentTouchPointerCount == 2 + && distanceSquared( + mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y, + mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y) + <= mLocked.pointerGestureMaxSwipeWidthSquared) { + const PointerData& current1 = mCurrentTouch.pointers[0]; + const PointerData& current2 = mCurrentTouch.pointers[1]; + const PointerData& origin1 = mPointerGesture.touchOrigin.pointers[ + mPointerGesture.touchOrigin.idToIndex[current1.id]]; + const PointerData& origin2 = mPointerGesture.touchOrigin.pointers[ + mPointerGesture.touchOrigin.idToIndex[current2.id]]; + + float x1 = (current1.x - origin1.x) * mLocked.pointerGestureXZoomScale; + float y1 = (current1.y - origin1.y) * mLocked.pointerGestureYZoomScale; + float x2 = (current2.x - origin2.x) * mLocked.pointerGestureXZoomScale; + float y2 = (current2.y - origin2.y) * mLocked.pointerGestureYZoomScale; + float magnitude1 = pythag(x1, y1); + float magnitude2 = pythag(x2, y2); + + // Calculate the dot product of the vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). + // We know that the magnitude is at least MULTITOUCH_MIN_TRAVEL because + // we checked it above. + float dot = x1 * x2 + y1 * y2; + float cosine = dot / (magnitude1 * magnitude2); // denominator always > 0 + if (cosine > SWIPE_TRANSITION_ANGLE_COSINE) { + mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } + } + + // Remember the initial centroid for the duration of the gesture. + mPointerGesture.initialCentroidX = 0; + mPointerGesture.initialCentroidY = 0; + for (uint32_t i = 0; i < currentTouchPointerCount; i++) { + const PointerData& touch = mCurrentTouch.pointers[i]; + mPointerGesture.initialCentroidX += touch.x; + mPointerGesture.initialCentroidY += touch.y; + } + mPointerGesture.initialCentroidX /= int32_t(currentTouchPointerCount); + mPointerGesture.initialCentroidY /= int32_t(currentTouchPointerCount); + + mPointerGesture.activeGestureId = 0; + } + } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // Switch to FREEFORM if additional pointers go down. + if (currentTouchPointerCount > 2) { + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; } } - // Check edge flags by looking only at the first pointer since the flags are - // global to the event. - if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { - uint32_t inIndex = touch->idToIndex[pointerIds[0]]; - const PointerData& in = touch->pointers[inIndex]; + if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // SWIPE mode. +#if DEBUG_GESTURES + LOGD("Gestures: SWIPE activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount); +#endif + assert(mPointerGesture.activeGestureId >= 0); + + float x = (mCurrentTouch.pointers[0].x + mCurrentTouch.pointers[1].x + - mPointerGesture.initialCentroidX * 2) * 0.5f + * mLocked.pointerGestureXMovementScale + mPointerGesture.initialPointerX; + float y = (mCurrentTouch.pointers[0].y + mCurrentTouch.pointers[1].y + - mPointerGesture.initialCentroidY * 2) * 0.5f + * mLocked.pointerGestureYMovementScale + mPointerGesture.initialPointerY; + + mPointerGesture.currentGesturePointerCount = 1; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + // FREEFORM mode. +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount); +#endif + assert(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGesturePointerCount = currentTouchPointerCount; + mPointerGesture.currentGestureIdBits.clear(); + + BitSet32 mappedTouchIdBits; + BitSet32 usedGestureIdBits; + if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + // Initially, assign the active gesture id to the active touch point + // if there is one. No other touch id bits are mapped yet. + if (!*outCancelPreviousGesture) { + mappedTouchIdBits.markBit(activeTouchId); + usedGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = + mPointerGesture.activeGestureId; + } else { + mPointerGesture.activeGestureId = -1; + } + } else { + // Otherwise, assume we mapped all touches from the previous frame. + // Reuse all mappings that are still applicable. + mappedTouchIdBits.value = mLastTouch.idBits.value & mCurrentTouch.idBits.value; + usedGestureIdBits = mPointerGesture.lastGestureIdBits; + + // Check whether we need to choose a new active gesture id because the + // current went went up. + for (BitSet32 upTouchIdBits(mLastTouch.idBits.value & ~mCurrentTouch.idBits.value); + !upTouchIdBits.isEmpty(); ) { + uint32_t upTouchId = upTouchIdBits.firstMarkedBit(); + upTouchIdBits.clearBit(upTouchId); + uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; + if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { + mPointerGesture.activeGestureId = -1; + break; + } + } + } - if (in.x <= mRawAxes.x.minValue) { - motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT, - mLocked.surfaceOrientation); - } else if (in.x >= mRawAxes.x.maxValue) { - motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT, - mLocked.surfaceOrientation); +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM follow up " + "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " + "activeGestureId=%d", + mappedTouchIdBits.value, usedGestureIdBits.value, + mPointerGesture.activeGestureId); +#endif + + for (uint32_t i = 0; i < currentTouchPointerCount; i++) { + uint32_t touchId = mCurrentTouch.pointers[i].id; + uint32_t gestureId; + if (!mappedTouchIdBits.hasBit(touchId)) { + gestureId = usedGestureIdBits.firstUnmarkedBit(); + usedGestureIdBits.markBit(gestureId); + mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM " + "new mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } else { + gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM " + "existing mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } + mPointerGesture.currentGestureIdBits.markBit(gestureId); + mPointerGesture.currentGestureIdToIndex[gestureId] = i; + + float x = (mCurrentTouch.pointers[i].x - mPointerGesture.initialCentroidX) + * mLocked.pointerGestureXZoomScale + mPointerGesture.initialPointerX; + float y = (mCurrentTouch.pointers[i].y - mPointerGesture.initialCentroidY) + * mLocked.pointerGestureYZoomScale + mPointerGesture.initialPointerY; + + mPointerGesture.currentGestureCoords[i].clear(); + mPointerGesture.currentGestureCoords[i].setAxisValue( + AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[i].setAxisValue( + AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[i].setAxisValue( + AMOTION_EVENT_AXIS_PRESSURE, 1.0f); } - if (in.y <= mRawAxes.y.minValue) { - motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP, - mLocked.surfaceOrientation); - } else if (in.y >= mRawAxes.y.maxValue) { - motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM, - mLocked.surfaceOrientation); + + if (mPointerGesture.activeGestureId < 0) { + mPointerGesture.activeGestureId = + mPointerGesture.currentGestureIdBits.firstMarkedBit(); +#if DEBUG_GESTURES + LOGD("Gestures: FREEFORM new " + "activeGestureId=%d", mPointerGesture.activeGestureId); +#endif } + } else { + // INDETERMINATE_MULTITOUCH mode. + // Do nothing. +#if DEBUG_GESTURES + LOGD("Gestures: INDETERMINATE_MULTITOUCH"); +#endif } + } - xPrecision = mLocked.orientedXPrecision; - yPrecision = mLocked.orientedYPrecision; - } // release lock + // Unfade the pointer if the user is doing anything with the touch pad. + mPointerController->setButtonState(mCurrentTouch.buttonState); + if (mCurrentTouch.buttonState || mCurrentTouch.pointerCount != 0) { + mPointerController->unfade(); + } + +#if DEBUG_GESTURES + LOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " + "currentGestureMode=%d, currentGesturePointerCount=%d, currentGestureIdBits=0x%08x, " + "lastGestureMode=%d, lastGesturePointerCount=%d, lastGestureIdBits=0x%08x", + toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), + mPointerGesture.currentGestureMode, mPointerGesture.currentGesturePointerCount, + mPointerGesture.currentGestureIdBits.value, + mPointerGesture.lastGestureMode, mPointerGesture.lastGesturePointerCount, + mPointerGesture.lastGestureIdBits.value); + for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; + LOGD(" currentGesture[%d]: index=%d, x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } + for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; + const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; + LOGD(" lastGesture[%d]: index=%d, x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } +#endif +} + +void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, + int32_t action, int32_t flags, uint32_t metaState, int32_t edgeFlags, + const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, + int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) { + PointerCoords pointerCoords[MAX_POINTERS]; + int32_t pointerIds[MAX_POINTERS]; + uint32_t pointerCount = 0; + while (!idBits.isEmpty()) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = idToIndex[id]; + pointerIds[pointerCount] = id; + pointerCoords[pointerCount].copyFrom(coords[index]); + + if (changedId >= 0 && id == uint32_t(changedId)) { + action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } + + pointerCount += 1; + } + + assert(pointerCount != 0); - getDispatcher()->notifyMotion(when, getDeviceId(), mTouchSource, policyFlags, - motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags, - pointerCount, pointerIds, pointerCoords, - xPrecision, yPrecision, mDownTime); + if (changedId >= 0 && pointerCount == 1) { + // Replace initial down and final up action. + // We can compare the action without masking off the changed pointer index + // because we know the index is 0. + if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { + action = AMOTION_EVENT_ACTION_DOWN; + } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { + action = AMOTION_EVENT_ACTION_UP; + } else { + // Can't happen. + assert(false); + } + } + + getDispatcher()->notifyMotion(when, getDeviceId(), source, policyFlags, + action, flags, metaState, edgeFlags, + pointerCount, pointerIds, pointerCoords, xPrecision, yPrecision, downTime); +} + +bool TouchInputMapper::updateMovedPointerCoords( + const PointerCoords* inCoords, const uint32_t* inIdToIndex, + PointerCoords* outCoords, const uint32_t* outIdToIndex, BitSet32 idBits) const { + bool changed = false; + while (!idBits.isEmpty()) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + uint32_t inIndex = inIdToIndex[id]; + uint32_t outIndex = outIdToIndex[id]; + const PointerCoords& curInCoords = inCoords[inIndex]; + PointerCoords& curOutCoords = outCoords[outIndex]; + + if (curInCoords != curOutCoords) { + curOutCoords.copyFrom(curInCoords); + changed = true; + } + } + return changed; +} + +void TouchInputMapper::fadePointer() { + { // acquire lock + AutoMutex _l(mLock); + if (mPointerController != NULL) { + mPointerController->fade(); + } + } // release lock } bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) { @@ -3592,6 +4584,7 @@ void SingleTouchInputMapper::initialize() { mY = 0; mPressure = 0; // default to 0 for devices that don't report pressure mToolWidth = 0; // default to 0 for devices that don't report tool width + mButtonState = 0; } void SingleTouchInputMapper::reset() { @@ -3611,6 +4604,19 @@ void SingleTouchInputMapper::process(const RawEvent* rawEvent) { // not have received valid position information yet. This logic assumes that // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet. break; + default: + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + uint32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode); + if (buttonState) { + if (rawEvent->value) { + mAccumulator.buttonDown |= buttonState; + } else { + mAccumulator.buttonUp |= buttonState; + } + mAccumulator.fields |= Accumulator::FIELD_BUTTONS; + } + } + break; } break; @@ -3671,6 +4677,10 @@ void SingleTouchInputMapper::sync(nsecs_t when) { mToolWidth = mAccumulator.absToolWidth; } + if (fields & Accumulator::FIELD_BUTTONS) { + mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp; + } + mCurrentTouch.clear(); if (mDown) { @@ -3686,6 +4696,7 @@ void SingleTouchInputMapper::sync(nsecs_t when) { mCurrentTouch.pointers[0].orientation = 0; mCurrentTouch.idToIndex[0] = 0; mCurrentTouch.idBits.markBit(0); + mCurrentTouch.buttonState = mButtonState; } syncTouch(when, true); @@ -3715,6 +4726,7 @@ MultiTouchInputMapper::~MultiTouchInputMapper() { void MultiTouchInputMapper::initialize() { mAccumulator.clear(); + mButtonState = 0; } void MultiTouchInputMapper::reset() { @@ -3725,6 +4737,20 @@ void MultiTouchInputMapper::reset() { void MultiTouchInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { + case EV_KEY: { + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + uint32_t buttonState = getButtonStateForScanCode(rawEvent->scanCode); + if (buttonState) { + if (rawEvent->value) { + mAccumulator.buttonDown |= buttonState; + } else { + mAccumulator.buttonUp |= buttonState; + } + } + } + break; + } + case EV_ABS: { uint32_t pointerIndex = mAccumulator.pointerCount; Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex]; @@ -3902,6 +4928,9 @@ void MultiTouchInputMapper::sync(nsecs_t when) { mCurrentTouch.pointerCount = outCount; + mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp; + mCurrentTouch.buttonState = mButtonState; + syncTouch(when, havePointerIds); mAccumulator.clear(); diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 68002ca1ab02..cf9b13de39c6 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -159,6 +159,8 @@ public: virtual void fadePointer() = 0; + virtual void requestTimeoutAtTime(nsecs_t when) = 0; + virtual InputReaderPolicyInterface* getPolicy() = 0; virtual InputDispatcherInterface* getDispatcher() = 0; virtual EventHubInterface* getEventHub() = 0; @@ -214,6 +216,10 @@ private: virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); } virtual EventHubInterface* getEventHub() { return mEventHub.get(); } + // The event queue. + static const int EVENT_BUFFER_SIZE = 256; + RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; + // This reader/writer lock guards the list of input devices. // The writer lock must be held whenever the list of input devices is modified // and then promptly released. @@ -226,15 +232,15 @@ private: KeyedVector<int32_t, InputDevice*> mDevices; // low-level input event decoding and device management - void process(const RawEvent* rawEvent); + void processEvents(const RawEvent* rawEvents, size_t count); void addDevice(int32_t deviceId); void removeDevice(int32_t deviceId); - void configureExcludedDevices(); - - void consumeEvent(const RawEvent* rawEvent); + void processEventsForDevice(int32_t deviceId, const RawEvent* rawEvents, size_t count); + void timeoutExpired(nsecs_t when); void handleConfigurationChanged(nsecs_t when); + void configureExcludedDevices(); // state management for all devices Mutex mStateLock; @@ -248,11 +254,14 @@ private: InputConfiguration mInputConfiguration; void updateInputConfiguration(); - nsecs_t mDisableVirtualKeysTimeout; + nsecs_t mDisableVirtualKeysTimeout; // only accessed by reader thread virtual void disableVirtualKeysUntil(nsecs_t time); virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, int32_t scanCode); + nsecs_t mNextTimeout; // only accessed by reader thread + virtual void requestTimeoutAtTime(nsecs_t when); + // state queries typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code, @@ -295,7 +304,8 @@ public: void addMapper(InputMapper* mapper); void configure(); void reset(); - void process(const RawEvent* rawEvent); + void process(const RawEvent* rawEvents, size_t count); + void timeoutExpired(nsecs_t when); void getDeviceInfo(InputDeviceInfo* outDeviceInfo); int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); @@ -352,6 +362,7 @@ public: virtual void configure(); virtual void reset(); virtual void process(const RawEvent* rawEvent) = 0; + virtual void timeoutExpired(nsecs_t when); virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); @@ -558,6 +569,8 @@ public: virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + virtual void fadePointer(); + protected: Mutex mLock; @@ -611,10 +624,12 @@ protected: PointerData pointers[MAX_POINTERS]; BitSet32 idBits; uint32_t idToIndex[MAX_POINTER_ID + 1]; + uint32_t buttonState; void copyFrom(const TouchData& other) { pointerCount = other.pointerCount; idBits = other.idBits; + buttonState = other.buttonState; for (uint32_t i = 0; i < pointerCount; i++) { pointers[i] = other.pointers[i]; @@ -627,17 +642,20 @@ protected: inline void clear() { pointerCount = 0; idBits.clear(); + buttonState = 0; } }; // Input sources supported by the device. uint32_t mTouchSource; // sources when reporting touch data + uint32_t mPointerSource; // sources when reporting pointer gestures // Immutable configuration parameters. struct Parameters { enum DeviceType { DEVICE_TYPE_TOUCH_SCREEN, DEVICE_TYPE_TOUCH_PAD, + DEVICE_TYPE_POINTER, }; DeviceType deviceType; @@ -735,11 +753,17 @@ protected: // Current and previous touch sample data. TouchData mCurrentTouch; + PointerCoords mCurrentTouchCoords[MAX_POINTERS]; + TouchData mLastTouch; + PointerCoords mLastTouchCoords[MAX_POINTERS]; // The time the primary pointer last went down. nsecs_t mDownTime; + // The pointer controller, or null if the device is not a pointer. + sp<PointerControllerInterface> mPointerController; + struct LockedState { Vector<VirtualKey> virtualKeys; @@ -804,6 +828,17 @@ protected: int32_t keyCode; int32_t scanCode; } currentVirtualKey; + + // Scale factor for gesture based pointer movements. + float pointerGestureXMovementScale; + float pointerGestureYMovementScale; + + // Scale factor for gesture based zooming and other freeform motions. + float pointerGestureXZoomScale; + float pointerGestureYZoomScale; + + // The maximum swipe width squared. + int32_t pointerGestureMaxSwipeWidthSquared; } mLocked; virtual void configureParameters(); @@ -869,13 +904,148 @@ private: uint64_t distance : 48; // squared distance }; + struct PointerGesture { + enum Mode { + // No fingers, button is not pressed. + // Nothing happening. + NEUTRAL, + + // No fingers, button is not pressed. + // Tap detected. + // Emits DOWN and UP events at the pointer location. + TAP, + + // Button is pressed. + // Pointer follows the active finger if there is one. Other fingers are ignored. + // Emits DOWN, MOVE and UP events at the pointer location. + CLICK_OR_DRAG, + + // Exactly one finger, button is not pressed. + // Pointer follows the active finger. + // Emits HOVER_MOVE events at the pointer location. + HOVER, + + // More than two fingers involved but they haven't moved enough for us + // to figure out what is intended. + INDETERMINATE_MULTITOUCH, + + // Exactly two fingers moving in the same direction, button is not pressed. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single pointer coordinate that + // follows the midpoint between both fingers. + // The centroid is fixed when entering this state. + SWIPE, + + // Two or more fingers moving in arbitrary directions, button is not pressed. + // Pointer does not move. + // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow + // each finger individually relative to the initial centroid of the finger. + // The centroid is fixed when entering this state. + FREEFORM, + + // Waiting for quiet time to end before starting the next gesture. + QUIET, + }; + + // The active pointer id from the raw touch data. + int32_t activeTouchId; // -1 if none + + // The active pointer id from the gesture last delivered to the application. + int32_t activeGestureId; // -1 if none + + // Pointer coords and ids for the current and previous pointer gesture. + Mode currentGestureMode; + uint32_t currentGesturePointerCount; + BitSet32 currentGestureIdBits; + uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerCoords currentGestureCoords[MAX_POINTERS]; + + Mode lastGestureMode; + uint32_t lastGesturePointerCount; + BitSet32 lastGestureIdBits; + uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerCoords lastGestureCoords[MAX_POINTERS]; + + // Tracks for all pointers originally went down. + TouchData touchOrigin; + + // Describes how touch ids are mapped to gesture ids for freeform gestures. + uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; + + // Initial centroid of the movement. + // Used to calculate how far the touch pointers have moved since the gesture started. + int32_t initialCentroidX; + int32_t initialCentroidY; + + // Initial pointer location. + // Used to track where the pointer was when the gesture started. + float initialPointerX; + float initialPointerY; + + // Time the pointer gesture last went down. + nsecs_t downTime; + + // Time we started waiting for a tap gesture. + nsecs_t tapTime; + + // Time we started waiting for quiescence. + nsecs_t quietTime; + + // A velocity tracker for determining whether to switch active pointers during drags. + VelocityTracker velocityTracker; + + void reset() { + activeTouchId = -1; + activeGestureId = -1; + currentGestureMode = NEUTRAL; + currentGesturePointerCount = 0; + currentGestureIdBits.clear(); + lastGestureMode = NEUTRAL; + lastGesturePointerCount = 0; + lastGestureIdBits.clear(); + touchOrigin.clear(); + initialCentroidX = 0; + initialCentroidY = 0; + initialPointerX = 0; + initialPointerY = 0; + downTime = 0; + velocityTracker.clear(); + resetTapTime(); + resetQuietTime(); + } + + void resetTapTime() { + tapTime = LLONG_MIN; + } + + void resetQuietTime() { + quietTime = LLONG_MIN; + } + } mPointerGesture; + void initializeLocked(); TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags); void dispatchTouches(nsecs_t when, uint32_t policyFlags); - void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch, - BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, - int32_t motionEventAction); + void prepareTouches(int32_t* outEdgeFlags, float* outXPrecision, float* outYPrecision); + void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags); + void preparePointerGestures(nsecs_t when, + bool* outCancelPreviousGesture, bool* outFinishPreviousGesture); + + // Dispatches a motion event. + // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the + // method will take care of setting the index and transmuting the action to DOWN or UP + // it is the first / last pointer to go down / up. + void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, + int32_t action, int32_t flags, uint32_t metaState, int32_t edgeFlags, + const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, + int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); + + // Updates pointer coords for pointers with specified ids that have moved. + // Returns true if any of them changed. + bool updateMovedPointerCoords(const PointerCoords* inCoords, const uint32_t* inIdToIndex, + PointerCoords* outCoords, const uint32_t* outIdToIndex, BitSet32 idBits) const; + void suppressSwipeOntoVirtualKeys(nsecs_t when); bool isPointInsideSurfaceLocked(int32_t x, int32_t y); @@ -907,6 +1077,7 @@ private: FIELD_ABS_Y = 4, FIELD_ABS_PRESSURE = 8, FIELD_ABS_TOOL_WIDTH = 16, + FIELD_BUTTONS = 32, }; uint32_t fields; @@ -917,8 +1088,13 @@ private: int32_t absPressure; int32_t absToolWidth; + uint32_t buttonDown; + uint32_t buttonUp; + inline void clear() { fields = 0; + buttonDown = 0; + buttonUp = 0; } } mAccumulator; @@ -927,6 +1103,7 @@ private: int32_t mY; int32_t mPressure; int32_t mToolWidth; + uint32_t mButtonState; void initialize(); @@ -978,12 +1155,20 @@ private: } } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks + // Bitfield of buttons that went down or up. + uint32_t buttonDown; + uint32_t buttonUp; + inline void clear() { pointerCount = 0; pointers[0].clear(); + buttonDown = 0; + buttonUp = 0; } } mAccumulator; + uint32_t mButtonState; + void initialize(); void sync(nsecs_t when); diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk index 799eb7689060..cabbccb37e04 100644 --- a/services/input/tests/Android.mk +++ b/services/input/tests/Android.mk @@ -15,7 +15,6 @@ shared_libraries := \ libhardware \ libhardware_legacy \ libui \ - libsurfaceflinger_client \ libskia \ libstlport \ libinput diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 32982c4f6569..60549c65fd0a 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -622,14 +622,14 @@ private: mExcludedDevices.add(String8(deviceName)); } - virtual bool getEvent(RawEvent* outEvent) { + virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { if (mEvents.empty()) { - return false; + return 0; } - *outEvent = *mEvents.begin(); + *buffer = *mEvents.begin(); mEvents.erase(mEvents.begin()); - return true; + return 1; } virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const { @@ -780,6 +780,9 @@ private: virtual void fadePointer() { } + + virtual void requestTimeoutAtTime(nsecs_t when) { + } }; @@ -1442,7 +1445,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe // Event handling. RawEvent event; - mDevice->process(&event); + mDevice->process(&event, 1); ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled()); ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled()); @@ -2460,8 +2463,18 @@ void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) { } -TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecified_ReturnsTouchPad) { +TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) { + SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); + prepareAxes(POSITION); + addMapperAndConfigure(mapper); + + ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper->getSources()); +} + +TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) { SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice); + mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X); + mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y); prepareAxes(POSITION); addMapperAndConfigure(mapper); diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index a4a95a0ace90..c03b994423aa 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -541,7 +541,9 @@ class AppWidgetService extends IAppWidgetService.Stub IRemoteViewsFactory.Stub.asInterface(service); try { cb.onDestroy(intent); - } catch (Exception e) { + } catch (RemoteException e) { + e.printStackTrace(); + } catch (RuntimeException e) { e.printStackTrace(); } mContext.unbindService(this); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index f170cb7358e2..12ac052617bf 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.DummyDataStateTracker; +import android.net.EthernetDataTracker; import android.net.IConnectivityManager; import android.net.LinkProperties; import android.net.MobileDataStateTracker; @@ -420,6 +421,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance(); mNetTrackers[netType].startMonitoring(context, mHandler); break; + case ConnectivityManager.TYPE_ETHERNET: + mNetTrackers[netType] = EthernetDataTracker.getInstance(); + mNetTrackers[netType].startMonitoring(context, mHandler); + break; default: loge("Trying to create a DataStateTracker for an unknown radio type " + mNetAttributes[netType].mRadio); diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java index 0fba7c3603af..fc5443e54d99 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -56,7 +56,7 @@ import android.util.Slog; * settings parameter with a default value of 2MB), the free memory is * logged to the event log. */ -class DeviceStorageMonitorService extends Binder { +public class DeviceStorageMonitorService extends Binder { private static final String TAG = "DeviceStorageMonitorService"; private static final boolean DEBUG = false; private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; @@ -99,7 +99,7 @@ class DeviceStorageMonitorService extends Binder { /** * This string is used for ServiceManager access to this class. */ - static final String SERVICE = "devicestoragemonitor"; + public static final String SERVICE = "devicestoragemonitor"; /** * Handler that checks the amount of disk space on the device and sends a @@ -398,4 +398,24 @@ class DeviceStorageMonitorService extends Binder { // force an early check postCheckMemoryMsg(true, 0); } + + /** + * Callable from other things in the system service to obtain the low memory + * threshold. + * + * @return low memory threshold in bytes + */ + public long getMemoryLowThreshold() { + return mMemLowThreshold; + } + + /** + * Callable from other things in the system process to check whether memory + * is low. + * + * @return true is memory is low + */ + public boolean isMemoryLow() { + return mLowMemFlag; + } } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 91ada6bf52b1..4e80147d0e64 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -18,6 +18,7 @@ package com.android.server; import com.android.internal.app.IMediaContainerService; import com.android.server.am.ActivityManagerService; +import com.android.server.pm.PackageManagerService; import android.Manifest; import android.content.BroadcastReceiver; @@ -86,6 +87,9 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC private static final String VOLD_TAG = "VoldConnector"; + /** Maximum number of ASEC containers allowed to be mounted. */ + private static final int MAX_CONTAINERS = 250; + /* * Internal vold volume state constants */ @@ -483,7 +487,6 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC } } }; - private final class MountServiceBinderListener implements IBinder.DeathRecipient { final IMountServiceListener mListener; @@ -1087,8 +1090,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC * amount of containers we'd ever expect to have. This keeps an * "asec list" from blocking a thread repeatedly. */ - mConnector = new NativeDaemonConnector(this, "vold", - PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG); + mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG); mReady = false; Thread thread = new Thread(mConnector, VOLD_TAG); thread.start(); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d1609630646d..4e1bbacb1d5f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -17,6 +17,7 @@ package com.android.server; import com.android.server.am.ActivityManagerService; +import com.android.server.pm.PackageManagerService; import com.android.server.usb.UsbService; import com.android.server.wm.WindowManagerService; import com.android.internal.app.ShutdownThread; diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 000023787548..915b679a041f 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -235,6 +235,15 @@ public class WifiService extends IWifiManager.Stub { } break; } + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { + if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { + Slog.d(TAG, "Send failed, client connection lost"); + } else { + Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); + } + mClients.remove((AsyncChannel) msg.obj); + break; + } case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { AsyncChannel ac = new AsyncChannel(); ac.connect(mContext, this, msg.replyTo); diff --git a/services/java/com/android/server/pm/BasePermission.java b/services/java/com/android/server/pm/BasePermission.java new file mode 100644 index 000000000000..4f27408a6fff --- /dev/null +++ b/services/java/com/android/server/pm/BasePermission.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.content.pm.PackageParser; +import android.content.pm.PermissionInfo; + +final class BasePermission { + final static int TYPE_NORMAL = 0; + + final static int TYPE_BUILTIN = 1; + + final static int TYPE_DYNAMIC = 2; + + final String name; + + String sourcePackage; + + PackageSettingBase packageSetting; + + final int type; + + int protectionLevel; + + PackageParser.Permission perm; + + PermissionInfo pendingInfo; + + int uid; + + int[] gids; + + BasePermission(String _name, String _sourcePackage, int _type) { + name = _name; + sourcePackage = _sourcePackage; + type = _type; + // Default to most conservative protection level. + protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; + } + + public String toString() { + return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name + + "}"; + } +} diff --git a/services/java/com/android/server/pm/GrantedPermissions.java b/services/java/com/android/server/pm/GrantedPermissions.java new file mode 100644 index 000000000000..c7629b98635e --- /dev/null +++ b/services/java/com/android/server/pm/GrantedPermissions.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.content.pm.ApplicationInfo; + +import java.util.HashSet; + +class GrantedPermissions { + int pkgFlags; + + HashSet<String> grantedPermissions = new HashSet<String>(); + + int[] gids; + + GrantedPermissions(int pkgFlags) { + setFlags(pkgFlags); + } + + @SuppressWarnings("unchecked") + GrantedPermissions(GrantedPermissions base) { + pkgFlags = base.pkgFlags; + grantedPermissions = (HashSet<String>) base.grantedPermissions.clone(); + + if (base.gids != null) { + gids = base.gids.clone(); + } + } + + void setFlags(int pkgFlags) { + this.pkgFlags = pkgFlags + & (ApplicationInfo.FLAG_SYSTEM + | ApplicationInfo.FLAG_FORWARD_LOCK + | ApplicationInfo.FLAG_EXTERNAL_STORAGE); + } +} diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/pm/Installer.java index 08d1b82f2520..8d40000b1571 100644 --- a/services/java/com/android/server/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -14,28 +14,31 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.pm; import android.content.pm.PackageStats; -import android.net.LocalSocketAddress; import android.net.LocalSocket; -import android.util.Config; +import android.net.LocalSocketAddress; import android.util.Slog; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.Socket; - class Installer { private static final String TAG = "Installer"; - InputStream mIn; - OutputStream mOut; - LocalSocket mSocket; - byte buf[] = new byte[1024]; - int buflen = 0; + private static final boolean LOCAL_DEBUG = true; + + InputStream mIn; + + OutputStream mOut; + + LocalSocket mSocket; + + byte buf[] = new byte[1024]; + + int buflen = 0; private boolean connect() { if (mSocket != null) { @@ -45,8 +48,8 @@ class Installer { try { mSocket = new LocalSocket(); - LocalSocketAddress address = new LocalSocketAddress( - "installd", LocalSocketAddress.Namespace.RESERVED); + LocalSocketAddress address = new LocalSocketAddress("installd", + LocalSocketAddress.Namespace.RESERVED); mSocket.connect(address); @@ -59,112 +62,131 @@ class Installer { return true; } - private void disconnect() { - Slog.i(TAG,"disconnecting..."); - try { - if (mSocket != null) mSocket.close(); - } catch (IOException ex) { } - try { - if (mIn != null) mIn.close(); - } catch (IOException ex) { } - try { - if (mOut != null) mOut.close(); - } catch (IOException ex) { } - mSocket = null; - mIn = null; - mOut = null; - } - - private boolean readBytes(byte buffer[], int len) { - int off = 0, count; - if (len < 0) return false; - while (off != len) { - try { - count = mIn.read(buffer, off, len - off); - if (count <= 0) { + private void disconnect() { + Slog.i(TAG, "disconnecting..."); + try { + if (mSocket != null) + mSocket.close(); + } catch (IOException ex) { + } + try { + if (mIn != null) + mIn.close(); + } catch (IOException ex) { + } + try { + if (mOut != null) + mOut.close(); + } catch (IOException ex) { + } + mSocket = null; + mIn = null; + mOut = null; + } + + private boolean readBytes(byte buffer[], int len) { + int off = 0, count; + if (len < 0) + return false; + while (off != len) { + try { + count = mIn.read(buffer, off, len - off); + if (count <= 0) { Slog.e(TAG, "read error " + count); break; } - off += count; - } catch (IOException ex) { - Slog.e(TAG,"read exception"); - break; - } - } -// Slog.i(TAG, "read "+len+" bytes"); - if (off == len) return true; - disconnect(); - return false; - } - - private boolean readReply() { - int len; - buflen = 0; - if (!readBytes(buf, 2)) return false; - len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); - if ((len < 1) || (len > 1024)) { - Slog.e(TAG,"invalid reply length ("+len+")"); - disconnect(); - return false; - } - if (!readBytes(buf, len)) return false; - buflen = len; - return true; - } - - private boolean writeCommand(String _cmd) { - byte[] cmd = _cmd.getBytes(); - int len = cmd.length; - if ((len < 1) || (len > 1024)) return false; - buf[0] = (byte) (len & 0xff); - buf[1] = (byte) ((len >> 8) & 0xff); - try { - mOut.write(buf, 0, 2); - mOut.write(cmd, 0, len); - } catch (IOException ex) { - Slog.e(TAG,"write error"); - disconnect(); - return false; - } - return true; - } - - private synchronized String transaction(String cmd) { - if (!connect()) { + off += count; + } catch (IOException ex) { + Slog.e(TAG, "read exception"); + break; + } + } + if (LOCAL_DEBUG) { + Slog.i(TAG, "read " + len + " bytes"); + } + if (off == len) + return true; + disconnect(); + return false; + } + + private boolean readReply() { + int len; + buflen = 0; + if (!readBytes(buf, 2)) + return false; + len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); + if ((len < 1) || (len > 1024)) { + Slog.e(TAG, "invalid reply length (" + len + ")"); + disconnect(); + return false; + } + if (!readBytes(buf, len)) + return false; + buflen = len; + return true; + } + + private boolean writeCommand(String _cmd) { + byte[] cmd = _cmd.getBytes(); + int len = cmd.length; + if ((len < 1) || (len > 1024)) + return false; + buf[0] = (byte) (len & 0xff); + buf[1] = (byte) ((len >> 8) & 0xff); + try { + mOut.write(buf, 0, 2); + mOut.write(cmd, 0, len); + } catch (IOException ex) { + Slog.e(TAG, "write error"); + disconnect(); + return false; + } + return true; + } + + private synchronized String transaction(String cmd) { + if (!connect()) { Slog.e(TAG, "connection failed"); return "-1"; } if (!writeCommand(cmd)) { - /* If installd died and restarted in the background - * (unlikely but possible) we'll fail on the next - * write (this one). Try to reconnect and write - * the command one more time before giving up. - */ + /* + * If installd died and restarted in the background (unlikely but + * possible) we'll fail on the next write (this one). Try to + * reconnect and write the command one more time before giving up. + */ Slog.e(TAG, "write command failed? reconnect!"); if (!connect() || !writeCommand(cmd)) { return "-1"; } } -// Slog.i(TAG,"send: '"+cmd+"'"); - if (readReply()) { + if (LOCAL_DEBUG) { + Slog.i(TAG, "send: '" + cmd + "'"); + } + if (readReply()) { String s = new String(buf, 0, buflen); -// Slog.i(TAG,"recv: '"+s+"'"); - return s; - } else { -// Slog.i(TAG,"fail"); - return "-1"; - } - } - - private int execute(String cmd) { - String res = transaction(cmd); - try { - return Integer.parseInt(res); - } catch (NumberFormatException ex) { - return -1; - } - } + if (LOCAL_DEBUG) { + Slog.i(TAG, "recv: '" + s + "'"); + } + return s; + } else { + if (LOCAL_DEBUG) { + Slog.i(TAG, "fail"); + } + return "-1"; + } + } + + private int execute(String cmd) { + String res = transaction(cmd); + try { + return Integer.parseInt(res); + } catch (NumberFormatException ex) { + return -1; + } + } public int install(String name, int uid, int gid) { StringBuilder builder = new StringBuilder("install"); @@ -225,14 +247,14 @@ class Installer { builder.append(name); return execute(builder.toString()); } - + public int clearUserData(String name) { StringBuilder builder = new StringBuilder("rmuserdata"); builder.append(' '); builder.append(name); return execute(builder.toString()); } - + public boolean ping() { if (execute("ping") < 0) { return false; @@ -240,7 +262,7 @@ class Installer { return true; } } - + public int freeCache(long freeStorageSize) { StringBuilder builder = new StringBuilder("freecache"); builder.append(' '); @@ -250,8 +272,8 @@ class Installer { /* * @param packagePathSuffix The name of the path relative to install - * directory. Say if the path name is /data/app/com.test-1.apk, - * the package suffix path will be com.test-1 + * directory. Say if the path name is /data/app/com.test-1.apk, the package + * suffix path will be com.test-1 */ public int setForwardLockPerm(String packagePathSuffix, int gid) { StringBuilder builder = new StringBuilder("protect"); @@ -261,7 +283,7 @@ class Installer { builder.append(gid); return execute(builder.toString()); } - + public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); @@ -275,7 +297,7 @@ class Installer { String s = transaction(builder.toString()); String res[] = s.split(" "); - if((res == null) || (res.length != 4)) { + if ((res == null) || (res.length != 4)) { return -1; } try { @@ -286,7 +308,7 @@ class Installer { } catch (NumberFormatException e) { return -1; } - } + } public int moveFiles() { return execute("movefiles"); diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index d54267347e36..669e060a1540 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -14,26 +14,30 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.pm; + +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; -import com.android.internal.util.FastXmlSerializer; -import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; +import com.android.server.DeviceStorageMonitorService; +import com.android.server.EventLogTags; +import com.android.server.IntentResolver; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.admin.IDevicePolicyManager; import android.app.backup.IBackupManager; -import android.content.Context; import android.content.ComponentName; +import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; import android.content.IntentFilter; @@ -42,7 +46,6 @@ import android.content.ServiceConnection; import android.content.IntentSender.SendIntentException; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.ComponentInfo; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; @@ -54,13 +57,10 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; -import android.content.pm.PackageStats; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import android.content.pm.PackageParser; -import android.content.pm.PermissionInfo; +import android.content.pm.PackageStats; import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -69,28 +69,32 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; -import android.os.Debug; +import android.os.Environment; +import android.os.FileObserver; +import android.os.FileUtils; +import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; -import android.os.RemoteException; -import android.os.Environment; -import android.os.FileObserver; -import android.os.FileUtils; -import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.security.SystemKeyStore; -import android.util.*; +import android.util.DisplayMetrics; +import android.util.EventLog; +import android.util.Log; +import android.util.LogPrinter; +import android.util.Slog; +import android.util.SparseArray; +import android.util.Xml; import android.view.Display; import android.view.WindowManager; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -129,25 +133,29 @@ import java.util.zip.ZipOutputStream; mmm frameworks/base/tests/AndroidTests adb install -r -f out/target/product/passion/data/app/AndroidTests.apk adb shell am instrument -w -e class com.android.unit_tests.PackageManagerTests com.android.unit_tests/android.test.InstrumentationTestRunner - * + * + * {@hide} */ -class PackageManagerService extends IPackageManager.Stub { - private static final String TAG = "PackageManager"; - private static final boolean DEBUG_SETTINGS = false; +public class PackageManagerService extends IPackageManager.Stub { + static final String TAG = "PackageManager"; + static final boolean DEBUG_SETTINGS = false; private static final boolean DEBUG_PREFERRED = false; - private static final boolean DEBUG_UPGRADE = false; + static final boolean DEBUG_UPGRADE = false; private static final boolean DEBUG_INSTALL = false; - private static final boolean DEBUG_STOPPED = false; - - private static final boolean MULTIPLE_APPLICATION_UIDS = true; + private static final boolean DEBUG_REMOVE = false; + private static final boolean DEBUG_SHOW_INFO = false; + private static final boolean DEBUG_PACKAGE_INFO = false; + private static final boolean DEBUG_INTENT_MATCHING = false; + private static final boolean DEBUG_PACKAGE_SCANNING = false; + private static final boolean DEBUG_APP_DIR_OBSERVER = false; + + static final boolean MULTIPLE_APPLICATION_UIDS = true; private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; - private static final int FIRST_APPLICATION_UID = + static final int FIRST_APPLICATION_UID = Process.FIRST_APPLICATION_UID; - private static final int MAX_APPLICATION_UIDS = 1000; - - private static final boolean SHOW_INFO = false; + static final int MAX_APPLICATION_UIDS = 1000; private static final boolean GET_CERTIFICATES = true; @@ -161,19 +169,6 @@ class PackageManagerService extends IPackageManager.Stub { // package apks to install directory. private static final String INSTALL_PACKAGE_SUFFIX = "-"; - /** - * Indicates the state of installation. Used by PackageManager to - * figure out incomplete installations. Say a package is being installed - * (the state is set to PKG_INSTALL_INCOMPLETE) and remains so till - * the package installation is successful or unsuccesful lin which case - * the PackageManager will no longer maintain state information associated - * with the package. If some exception(like device freeze or battery being - * pulled out) occurs during installation of a package, the PackageManager - * needs this information to clean up the previously failed installation. - */ - private static final int PKG_INSTALL_INCOMPLETE = 0; - private static final int PKG_INSTALL_COMPLETE = 1; - static final int SCAN_MONITOR = 1<<0; static final int SCAN_NO_DEX = 1<<1; static final int SCAN_FORCE_DEX = 1<<2; @@ -362,6 +357,7 @@ class PackageManagerService extends IPackageManager.Stub { // Delay time in millisecs static final int BROADCAST_DELAY = 10 * 1000; + final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection(); class DefaultContainerConnection implements ServiceConnection { @@ -526,12 +522,12 @@ class PackageManagerService extends IPackageManager.Stub { } case MCS_GIVE_UP: { if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_giveup too many retries"); - HandlerParams params = mPendingInstalls.remove(0); + mPendingInstalls.remove(0); break; } case SEND_PENDING_BROADCAST : { String packages[]; - ArrayList components[]; + ArrayList<String> components[]; int size = 0; int uids[]; Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); @@ -563,8 +559,7 @@ class PackageManagerService extends IPackageManager.Stub { } // Send broadcasts for (int i = 0; i < size; i++) { - sendPackageChangedBroadcast(packages[i], true, - (ArrayList<String>)components[i], uids[i]); + sendPackageChangedBroadcast(packages[i], true, components[i], uids[i]); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); break; @@ -662,7 +657,7 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { removeMessages(WRITE_SETTINGS); removeMessages(WRITE_STOPPED_PACKAGES); - mSettings.writeLP(); + mSettings.writeLPr(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; @@ -670,7 +665,7 @@ class PackageManagerService extends IPackageManager.Stub { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { removeMessages(WRITE_STOPPED_PACKAGES); - mSettings.writeStoppedLP(); + mSettings.writeStoppedLPr(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; @@ -742,17 +737,17 @@ class PackageManagerService extends IPackageManager.Stub { mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); mSettings = new Settings(); - mSettings.addSharedUserLP("android.uid.system", + mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM); - mSettings.addSharedUserLP("android.uid.phone", + mSettings.addSharedUserLPw("android.uid.phone", MULTIPLE_APPLICATION_UIDS ? RADIO_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); - mSettings.addSharedUserLP("android.uid.log", + mSettings.addSharedUserLPw("android.uid.log", MULTIPLE_APPLICATION_UIDS ? LOG_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); - mSettings.addSharedUserLP("android.uid.nfc", + mSettings.addSharedUserLPw("android.uid.nfc", MULTIPLE_APPLICATION_UIDS ? NFC_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); @@ -790,6 +785,7 @@ class PackageManagerService extends IPackageManager.Stub { d.getMetrics(mMetrics); synchronized (mInstallLock) { + // writer synchronized (mPackages) { mHandlerThread.start(); mHandler = new PackageHandler(mHandlerThread.getLooper()); @@ -810,7 +806,7 @@ class PackageManagerService extends IPackageManager.Stub { readPermissions(); - mRestoredSettings = mSettings.readLP(); + mRestoredSettings = mSettings.readLPw(); long startTime = SystemClock.uptimeMillis(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, @@ -985,7 +981,7 @@ class PackageManagerService extends IPackageManager.Stub { mAppInstallDir.mkdirs(); // scanDirLI() assumes this dir exists } //look for any incomplete package installations - ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackages(); + ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr(); //clean up list for(int i = 0; i < deletePkgsList.size(); i++) { //clean up here @@ -1026,9 +1022,10 @@ class PackageManagerService extends IPackageManager.Stub { + "; regranting permissions for internal storage"); mSettings.mInternalSdkPlatform = mSdkVersion; - updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions); + updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions); - mSettings.writeLP(); + // can downgrade to reader + mSettings.writeLPr(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); @@ -1078,7 +1075,7 @@ class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Unable to remove old code file: " + ps.resourcePath); } } - mSettings.removePackageLP(ps.name); + mSettings.removePackageLPw(ps.name); } void readPermissions() { @@ -1345,16 +1342,16 @@ class PackageManagerService extends IPackageManager.Stub { } public PackageInfo getPackageInfo(String packageName, int flags) { + // reader synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); - if (Config.LOGV) Log.v( - TAG, "getPackageInfo " + packageName - + ": " + p); + if (DEBUG_PACKAGE_INFO) + Log.v(TAG, "getPackageInfo " + packageName + ": " + p); if (p != null) { return generatePackageInfo(p, flags); } if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { - return generatePackageInfoFromSettingsLP(packageName, flags); + return generatePackageInfoFromSettingsLPw(packageName, flags); } } return null; @@ -1362,6 +1359,7 @@ class PackageManagerService extends IPackageManager.Stub { public String[] currentToCanonicalPackageNames(String[] names) { String[] out = new String[names.length]; + // reader synchronized (mPackages) { for (int i=names.length-1; i>=0; i--) { PackageSetting ps = mSettings.mPackages.get(names[i]); @@ -1373,6 +1371,7 @@ class PackageManagerService extends IPackageManager.Stub { public String[] canonicalToCurrentPackageNames(String[] names) { String[] out = new String[names.length]; + // reader synchronized (mPackages) { for (int i=names.length-1; i>=0; i--) { String cur = mSettings.mRenamedPackages.get(names[i]); @@ -1383,6 +1382,7 @@ class PackageManagerService extends IPackageManager.Stub { } public int getPackageUid(String packageName) { + // reader synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); if(p != null) { @@ -1398,11 +1398,11 @@ class PackageManagerService extends IPackageManager.Stub { } public int[] getPackageGids(String packageName) { + // reader synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); - if (Config.LOGV) Log.v( - TAG, "getPackageGids" + packageName - + ": " + p); + if (DEBUG_PACKAGE_INFO) + Log.v(TAG, "getPackageGids" + packageName + ": " + p); if (p != null) { final PackageSetting ps = (PackageSetting)p.mExtras; final SharedUserSetting suid = ps.sharedUser; @@ -1427,6 +1427,7 @@ class PackageManagerService extends IPackageManager.Stub { } public PermissionInfo getPermissionInfo(String name, int flags) { + // reader synchronized (mPackages) { final BasePermission p = mSettings.mPermissions.get(name); if (p != null) { @@ -1437,6 +1438,7 @@ class PackageManagerService extends IPackageManager.Stub { } public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) { + // reader synchronized (mPackages) { ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); for (BasePermission p : mSettings.mPermissions.values()) { @@ -1459,6 +1461,7 @@ class PackageManagerService extends IPackageManager.Stub { } public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) { + // reader synchronized (mPackages) { return PackageParser.generatePermissionGroupInfo( mPermissionGroups.get(name), flags); @@ -1466,6 +1469,7 @@ class PackageManagerService extends IPackageManager.Stub { } public List<PermissionGroupInfo> getAllPermissionGroups(int flags) { + // reader synchronized (mPackages) { final int N = mPermissionGroups.size(); ArrayList<PermissionGroupInfo> out @@ -1477,12 +1481,12 @@ class PackageManagerService extends IPackageManager.Stub { } } - private ApplicationInfo generateApplicationInfoFromSettingsLP(String packageName, int flags) { + private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags) { PackageSetting ps = mSettings.mPackages.get(packageName); - if(ps != null) { - if(ps.pkg == null) { - PackageInfo pInfo = generatePackageInfoFromSettingsLP(packageName, flags); - if(pInfo != null) { + if (ps != null) { + if (ps.pkg == null) { + PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags); + if (pInfo != null) { return pInfo.applicationInfo; } return null; @@ -1492,10 +1496,10 @@ class PackageManagerService extends IPackageManager.Stub { return null; } - private PackageInfo generatePackageInfoFromSettingsLP(String packageName, int flags) { + private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags) { PackageSetting ps = mSettings.mPackages.get(packageName); - if(ps != null) { - if(ps.pkg == null) { + if (ps != null) { + if (ps.pkg == null) { ps.pkg = new PackageParser.Package(packageName); ps.pkg.applicationInfo.packageName = packageName; ps.pkg.applicationInfo.flags = ps.pkgFlags; @@ -1512,9 +1516,10 @@ class PackageManagerService extends IPackageManager.Stub { } public ApplicationInfo getApplicationInfo(String packageName, int flags) { + // writer synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); - if (Config.LOGV) Log.v( + if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getApplicationInfo " + packageName + ": " + p); if (p != null) { @@ -1525,7 +1530,7 @@ class PackageManagerService extends IPackageManager.Stub { return mAndroidApplication; } if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { - return generateApplicationInfoFromSettingsLP(packageName, flags); + return generateApplicationInfoFromSettingsLPw(packageName, flags); } } return null; @@ -1589,8 +1594,8 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); - if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a); - if (a != null && mSettings.isEnabledLP(a.info, flags)) { + if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a); + if (a != null && mSettings.isEnabledLPr(a.info, flags)) { return PackageParser.generateActivityInfo(a, flags); } if (mResolveComponentName.equals(component)) { @@ -1603,9 +1608,9 @@ class PackageManagerService extends IPackageManager.Stub { public ActivityInfo getReceiverInfo(ComponentName component, int flags) { synchronized (mPackages) { PackageParser.Activity a = mReceivers.mActivities.get(component); - if (Config.LOGV) Log.v( + if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getReceiverInfo " + component + ": " + a); - if (a != null && mSettings.isEnabledLP(a.info, flags)) { + if (a != null && mSettings.isEnabledLPr(a.info, flags)) { return PackageParser.generateActivityInfo(a, flags); } } @@ -1615,9 +1620,9 @@ class PackageManagerService extends IPackageManager.Stub { public ServiceInfo getServiceInfo(ComponentName component, int flags) { synchronized (mPackages) { PackageParser.Service s = mServices.mServices.get(component); - if (Config.LOGV) Log.v( + if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getServiceInfo " + component + ": " + s); - if (s != null && mSettings.isEnabledLP(s.info, flags)) { + if (s != null && mSettings.isEnabledLPr(s.info, flags)) { return PackageParser.generateServiceInfo(s, flags); } } @@ -1627,9 +1632,9 @@ class PackageManagerService extends IPackageManager.Stub { public ProviderInfo getProviderInfo(ComponentName component, int flags) { synchronized (mPackages) { PackageParser.Provider p = mProvidersByComponent.get(component); - if (Config.LOGV) Log.v( + if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getProviderInfo " + component + ": " + p); - if (p != null && mSettings.isEnabledLP(p.info, flags)) { + if (p != null && mSettings.isEnabledLPr(p.info, flags)) { return PackageParser.generateProviderInfo(p, flags); } } @@ -1693,7 +1698,7 @@ class PackageManagerService extends IPackageManager.Stub { public int checkUidPermission(String permName, int uid) { synchronized (mPackages) { - Object obj = mSettings.getUserIdLP(uid); + Object obj = mSettings.getUserIdLPr(uid); if (obj != null) { GrantedPermissions gp = (GrantedPermissions)obj; if (gp.grantedPermissions.contains(permName)) { @@ -1798,7 +1803,7 @@ class PackageManagerService extends IPackageManager.Stub { } if (changed) { if (!async) { - mSettings.writeLP(); + mSettings.writeLPr(); } else { scheduleWriteSettingsLocked(); } @@ -1829,7 +1834,7 @@ class PackageManagerService extends IPackageManager.Stub { + name); } mSettings.mPermissions.remove(name); - mSettings.writeLP(); + mSettings.writeLPr(); } } } @@ -1842,21 +1847,22 @@ class PackageManagerService extends IPackageManager.Stub { public int checkSignatures(String pkg1, String pkg2) { synchronized (mPackages) { - PackageParser.Package p1 = mPackages.get(pkg1); - PackageParser.Package p2 = mPackages.get(pkg2); + final PackageParser.Package p1 = mPackages.get(pkg1); + final PackageParser.Package p2 = mPackages.get(pkg2); if (p1 == null || p1.mExtras == null || p2 == null || p2.mExtras == null) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } - return checkSignaturesLP(p1.mSignatures, p2.mSignatures); + return compareSignatures(p1.mSignatures, p2.mSignatures); } } public int checkUidSignatures(int uid1, int uid2) { + // reader synchronized (mPackages) { Signature[] s1; Signature[] s2; - Object obj = mSettings.getUserIdLP(uid1); + Object obj = mSettings.getUserIdLPr(uid1); if (obj != null) { if (obj instanceof SharedUserSetting) { s1 = ((SharedUserSetting)obj).signatures.mSignatures; @@ -1868,7 +1874,7 @@ class PackageManagerService extends IPackageManager.Stub { } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } - obj = mSettings.getUserIdLP(uid2); + obj = mSettings.getUserIdLPr(uid2); if (obj != null) { if (obj instanceof SharedUserSetting) { s2 = ((SharedUserSetting)obj).signatures.mSignatures; @@ -1880,11 +1886,11 @@ class PackageManagerService extends IPackageManager.Stub { } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } - return checkSignaturesLP(s1, s2); + return compareSignatures(s1, s2); } } - int checkSignaturesLP(Signature[] s1, Signature[] s2) { + static int compareSignatures(Signature[] s1, Signature[] s2) { if (s1 == null) { return s2 == null ? PackageManager.SIGNATURE_NEITHER_SIGNED @@ -1909,20 +1915,21 @@ class PackageManagerService extends IPackageManager.Stub { } public String[] getPackagesForUid(int uid) { + // reader synchronized (mPackages) { - Object obj = mSettings.getUserIdLP(uid); + Object obj = mSettings.getUserIdLPr(uid); if (obj instanceof SharedUserSetting) { - SharedUserSetting sus = (SharedUserSetting)obj; + final SharedUserSetting sus = (SharedUserSetting) obj; final int N = sus.packages.size(); - String[] res = new String[N]; - Iterator<PackageSetting> it = sus.packages.iterator(); - int i=0; + final String[] res = new String[N]; + final Iterator<PackageSetting> it = sus.packages.iterator(); + int i = 0; while (it.hasNext()) { res[i++] = it.next().name; } return res; } else if (obj instanceof PackageSetting) { - PackageSetting ps = (PackageSetting)obj; + final PackageSetting ps = (PackageSetting) obj; return new String[] { ps.name }; } } @@ -1930,13 +1937,14 @@ class PackageManagerService extends IPackageManager.Stub { } public String getNameForUid(int uid) { + // reader synchronized (mPackages) { - Object obj = mSettings.getUserIdLP(uid); + Object obj = mSettings.getUserIdLPr(uid); if (obj instanceof SharedUserSetting) { - SharedUserSetting sus = (SharedUserSetting)obj; + final SharedUserSetting sus = (SharedUserSetting) obj; return sus.name + ":" + sus.userId; } else if (obj instanceof PackageSetting) { - PackageSetting ps = (PackageSetting)obj; + final PackageSetting ps = (PackageSetting) obj; return ps.name; } } @@ -1947,8 +1955,9 @@ class PackageManagerService extends IPackageManager.Stub { if(sharedUserName == null) { return -1; } + // reader synchronized (mPackages) { - SharedUserSetting suid = mSettings.getSharedUserLP(sharedUserName, 0, false); + final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, false); if(suid == null) { return -1; } @@ -1973,11 +1982,9 @@ class PackageManagerService extends IPackageManager.Stub { // then let the user decide between them. ResolveInfo r0 = query.get(0); ResolveInfo r1 = query.get(1); - if (false) { - System.out.println(r0.activityInfo.name + - "=" + r0.priority + " vs " + - r1.activityInfo.name + - "=" + r1.priority); + if (DEBUG_INTENT_MATCHING) { + Log.d(TAG, r0.activityInfo.name + "=" + r0.priority + " vs " + + r1.activityInfo.name + "=" + r1.priority); } // If the first activity has a higher priority, or a different // default, then it is always desireable to pick it. @@ -2001,6 +2008,7 @@ class PackageManagerService extends IPackageManager.Stub { ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags, List<ResolveInfo> query, int priority) { + // writer synchronized (mPackages) { if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); List<PreferredActivity> prefs = @@ -2011,24 +2019,35 @@ class PackageManagerService extends IPackageManager.Stub { // We will only allow preferred activities that came // from the same match quality. int match = 0; + + if (DEBUG_PREFERRED) { + Log.v(TAG, "Figuring out best match..."); + } + final int N = query.size(); - if (DEBUG_PREFERRED) Log.v(TAG, "Figuring out best match..."); for (int j=0; j<N; j++) { - ResolveInfo ri = query.get(j); - if (DEBUG_PREFERRED) Log.v(TAG, "Match for " + ri.activityInfo - + ": 0x" + Integer.toHexString(match)); - if (ri.match > match) match = ri.match; + final ResolveInfo ri = query.get(j); + if (DEBUG_PREFERRED) { + Log.v(TAG, "Match for " + ri.activityInfo + ": 0x" + + Integer.toHexString(match)); + } + if (ri.match > match) { + match = ri.match; + } } - if (DEBUG_PREFERRED) Log.v(TAG, "Best match: 0x" - + Integer.toHexString(match)); + + if (DEBUG_PREFERRED) { + Log.v(TAG, "Best match: 0x" + Integer.toHexString(match)); + } + match &= IntentFilter.MATCH_CATEGORY_MASK; final int M = prefs.size(); for (int i=0; i<M; i++) { - PreferredActivity pa = prefs.get(i); + final PreferredActivity pa = prefs.get(i); if (pa.mPref.mMatch != match) { continue; } - ActivityInfo ai = getActivityInfo(pa.mPref.mComponent, flags); + final ActivityInfo ai = getActivityInfo(pa.mPref.mComponent, flags); if (DEBUG_PREFERRED) { Log.v(TAG, "Got preferred activity:"); if (ai != null) { @@ -2039,7 +2058,7 @@ class PackageManagerService extends IPackageManager.Stub { } if (ai != null) { for (int j=0; j<N; j++) { - ResolveInfo ri = query.get(j); + final ResolveInfo ri = query.get(j); if (!ri.activityInfo.applicationInfo.packageName .equals(ai.applicationInfo.packageName)) { continue; @@ -2071,28 +2090,28 @@ class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags) { - ComponentName comp = intent.getComponent(); + final ComponentName comp = intent.getComponent(); if (comp != null) { - List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); - ActivityInfo ai = getActivityInfo(comp, flags); + final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); + final ActivityInfo ai = getActivityInfo(comp, flags); if (ai != null) { - ResolveInfo ri = new ResolveInfo(); + final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } return list; } + // reader synchronized (mPackages) { - String pkgName = intent.getPackage(); + final String pkgName = intent.getPackage(); if (pkgName == null) { - return (List<ResolveInfo>)mActivities.queryIntent(intent, - resolvedType, flags); + return mActivities.queryIntent(intent, resolvedType, flags); } - PackageParser.Package pkg = mPackages.get(pkgName); + final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { - return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent, - resolvedType, flags, pkg.activities); + return mActivities.queryIntentForPackage(intent, resolvedType, flags, + pkg.activities); } return new ArrayList<ResolveInfo>(); } @@ -2103,9 +2122,12 @@ class PackageManagerService extends IPackageManager.Stub { String resolvedType, int flags) { final String resultsAction = intent.getAction(); - List<ResolveInfo> results = queryIntentActivities( - intent, resolvedType, flags|PackageManager.GET_RESOLVED_FILTER); - if (Config.LOGV) Log.v(TAG, "Query " + intent + ": " + results); + List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags + | PackageManager.GET_RESOLVED_FILTER); + + if (DEBUG_INTENT_MATCHING) { + Log.v(TAG, "Query " + intent + ": " + results); + } int specificsPos = 0; int N; @@ -2125,16 +2147,21 @@ class PackageManagerService extends IPackageManager.Stub { continue; } - if (Config.LOGV) Log.v(TAG, "Specific #" + i + ": " + sintent); + if (DEBUG_INTENT_MATCHING) { + Log.v(TAG, "Specific #" + i + ": " + sintent); + } + String action = sintent.getAction(); if (resultsAction != null && resultsAction.equals(action)) { // If this action was explicitly requested, then don't // remove things that have it. action = null; } - ComponentName comp = sintent.getComponent(); + ResolveInfo ri = null; ActivityInfo ai = null; + + ComponentName comp = sintent.getComponent(); if (comp == null) { ri = resolveIntent( sintent, @@ -2158,7 +2185,7 @@ class PackageManagerService extends IPackageManager.Stub { // Look for any generic query activities that are duplicates // of this specific one, and remove them from the results. - if (Config.LOGV) Log.v(TAG, "Specific #" + i + ": " + ai); + if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Specific #" + i + ": " + ai); N = results.size(); int j; for (j=specificsPos; j<N; j++) { @@ -2168,7 +2195,7 @@ class PackageManagerService extends IPackageManager.Stub { comp.getPackageName())) || (action != null && sri.filter.matchAction(action))) { results.remove(j); - if (Config.LOGV) Log.v( + if (DEBUG_INTENT_MATCHING) Log.v( TAG, "Removing duplicate item from " + j + " due to specific " + specificsPos); if (ri == null) { @@ -2216,7 +2243,7 @@ class PackageManagerService extends IPackageManager.Stub { final ResolveInfo rij = results.get(j); if (rij.filter != null && rij.filter.hasAction(action)) { results.remove(j); - if (Config.LOGV) Log.v( + if (DEBUG_INTENT_MATCHING) Log.v( TAG, "Removing duplicate item from " + j + " due to action " + action + " at " + i); j--; @@ -2255,12 +2282,11 @@ class PackageManagerService extends IPackageManager.Stub { } } - if (Config.LOGV) Log.v(TAG, "Result: " + results); + if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Result: " + results); return results; } - public List<ResolveInfo> queryIntentReceivers(Intent intent, - String resolvedType, int flags) { + public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) { ComponentName comp = intent.getComponent(); if (comp != null) { List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); @@ -2273,25 +2299,22 @@ class PackageManagerService extends IPackageManager.Stub { return list; } + // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { - return (List<ResolveInfo>)mReceivers.queryIntent(intent, - resolvedType, flags); + return mReceivers.queryIntent(intent, resolvedType, flags); } - PackageParser.Package pkg = mPackages.get(pkgName); + final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { - return (List<ResolveInfo>) mReceivers.queryIntentForPackage(intent, - resolvedType, flags, pkg.receivers); + return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers); } return null; } } - public ResolveInfo resolveService(Intent intent, String resolvedType, - int flags) { - List<ResolveInfo> query = queryIntentServices(intent, resolvedType, - flags); + public ResolveInfo resolveService(Intent intent, String resolvedType, int flags) { + List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, @@ -2302,56 +2325,54 @@ class PackageManagerService extends IPackageManager.Stub { return null; } - public List<ResolveInfo> queryIntentServices(Intent intent, - String resolvedType, int flags) { - ComponentName comp = intent.getComponent(); + public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags) { + final ComponentName comp = intent.getComponent(); if (comp != null) { - List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); - ServiceInfo si = getServiceInfo(comp, flags); + final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); + final ServiceInfo si = getServiceInfo(comp, flags); if (si != null) { - ResolveInfo ri = new ResolveInfo(); + final ResolveInfo ri = new ResolveInfo(); ri.serviceInfo = si; list.add(ri); } return list; } + // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { - return (List<ResolveInfo>)mServices.queryIntent(intent, - resolvedType, flags); + return mServices.queryIntent(intent, resolvedType, flags); } - PackageParser.Package pkg = mPackages.get(pkgName); + final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { - return (List<ResolveInfo>)mServices.queryIntentForPackage(intent, - resolvedType, flags, pkg.services); + return mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services); } return null; } } public List<PackageInfo> getInstalledPackages(int flags) { - ArrayList<PackageInfo> finalList = new ArrayList<PackageInfo>(); + final ArrayList<PackageInfo> finalList = new ArrayList<PackageInfo>(); + // writer synchronized (mPackages) { if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { - Iterator<PackageSetting> i = mSettings.mPackages.values().iterator(); + final Iterator<PackageSetting> i = mSettings.mPackages.values().iterator(); while (i.hasNext()) { final PackageSetting ps = i.next(); - PackageInfo psPkg = generatePackageInfoFromSettingsLP(ps.name, flags); - if(psPkg != null) { + final PackageInfo psPkg = generatePackageInfoFromSettingsLPw(ps.name, flags); + if (psPkg != null) { finalList.add(psPkg); } } - } - else { - Iterator<PackageParser.Package> i = mPackages.values().iterator(); + } else { + final Iterator<PackageParser.Package> i = mPackages.values().iterator(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo != null) { - PackageInfo pi = generatePackageInfo(p, flags); - if(pi != null) { + final PackageInfo pi = generatePackageInfo(p, flags); + if (pi != null) { finalList.add(pi); } } @@ -2362,20 +2383,20 @@ class PackageManagerService extends IPackageManager.Stub { } public List<ApplicationInfo> getInstalledApplications(int flags) { - ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>(); + final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>(); + // writer synchronized(mPackages) { if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { - Iterator<PackageSetting> i = mSettings.mPackages.values().iterator(); + final Iterator<PackageSetting> i = mSettings.mPackages.values().iterator(); while (i.hasNext()) { final PackageSetting ps = i.next(); - ApplicationInfo ai = generateApplicationInfoFromSettingsLP(ps.name, flags); + ApplicationInfo ai = generateApplicationInfoFromSettingsLPw(ps.name, flags); if(ai != null) { finalList.add(ai); } } - } - else { - Iterator<PackageParser.Package> i = mPackages.values().iterator(); + } else { + final Iterator<PackageParser.Package> i = mPackages.values().iterator(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo != null) { @@ -2391,12 +2412,13 @@ class PackageManagerService extends IPackageManager.Stub { } public List<ApplicationInfo> getPersistentApplications(int flags) { - ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>(); + final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>(); + // reader synchronized (mPackages) { - Iterator<PackageParser.Package> i = mPackages.values().iterator(); + final Iterator<PackageParser.Package> i = mPackages.values().iterator(); while (i.hasNext()) { - PackageParser.Package p = i.next(); + final PackageParser.Package p = i.next(); if (p.applicationInfo != null && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p))) { @@ -2409,10 +2431,11 @@ class PackageManagerService extends IPackageManager.Stub { } public ProviderInfo resolveContentProvider(String name, int flags) { + // reader synchronized (mPackages) { final PackageParser.Provider provider = mProviders.get(name); return provider != null - && mSettings.isEnabledLP(provider.info, flags) + && mSettings.isEnabledLPr(provider.info, flags) && (!mSafeMode || (provider.info.applicationInfo.flags &ApplicationInfo.FLAG_SYSTEM) != 0) ? PackageParser.generateProviderInfo(provider, flags) @@ -2423,10 +2446,12 @@ class PackageManagerService extends IPackageManager.Stub { /** * @deprecated */ - public void querySyncProviders(List outNames, List outInfo) { + @Deprecated + public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) { + // reader synchronized (mPackages) { - Iterator<Map.Entry<String, PackageParser.Provider>> i - = mProviders.entrySet().iterator(); + final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet() + .iterator(); while (i.hasNext()) { Map.Entry<String, PackageParser.Provider> entry = i.next(); @@ -2446,22 +2471,22 @@ class PackageManagerService extends IPackageManager.Stub { int uid, int flags) { ArrayList<ProviderInfo> finalList = null; + // reader synchronized (mPackages) { - Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator(); + final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator(); while (i.hasNext()) { - PackageParser.Provider p = i.next(); + final PackageParser.Provider p = i.next(); if (p.info.authority != null - && (processName == null || - (p.info.processName.equals(processName) - && p.info.applicationInfo.uid == uid)) - && mSettings.isEnabledLP(p.info, flags) - && (!mSafeMode || (p.info.applicationInfo.flags - &ApplicationInfo.FLAG_SYSTEM) != 0)) { + && (processName == null + || (p.info.processName.equals(processName) + && p.info.applicationInfo.uid == uid)) + && mSettings.isEnabledLPr(p.info, flags) + && (!mSafeMode + || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) { if (finalList == null) { finalList = new ArrayList<ProviderInfo>(3); } - finalList.add(PackageParser.generateProviderInfo(p, - flags)); + finalList.add(PackageParser.generateProviderInfo(p, flags)); } } } @@ -2475,6 +2500,7 @@ class PackageManagerService extends IPackageManager.Stub { public InstrumentationInfo getInstrumentationInfo(ComponentName name, int flags) { + // reader synchronized (mPackages) { final PackageParser.Instrumentation i = mInstrumentation.get(name); return PackageParser.generateInstrumentationInfo(i, flags); @@ -2486,10 +2512,11 @@ class PackageManagerService extends IPackageManager.Stub { ArrayList<InstrumentationInfo> finalList = new ArrayList<InstrumentationInfo>(); + // reader synchronized (mPackages) { - Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator(); + final Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator(); while (i.hasNext()) { - PackageParser.Instrumentation p = i.next(); + final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { finalList.add(PackageParser.generateInstrumentationInfo(p, @@ -2508,7 +2535,7 @@ class PackageManagerService extends IPackageManager.Stub { return; } - if (false) { + if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + dir); } @@ -2538,7 +2565,7 @@ class PackageManagerService extends IPackageManager.Stub { return fname; } - private static void reportSettingsProblem(int priority, String msg) { + static void reportSettingsProblem(int priority, String msg) { try { File fname = getSettingsProblemFile(); FileOutputStream out = new FileOutputStream(fname, true); @@ -2602,17 +2629,18 @@ class PackageManagerService extends IPackageManager.Stub { } PackageSetting ps = null; PackageSetting updatedPkg; + // reader synchronized (mPackages) { // Look to see if we already know about this package. String oldName = mSettings.mRenamedPackages.get(pkg.packageName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) { // This package has been renamed to its original name. Let's // use that. - ps = mSettings.peekPackageLP(oldName); + ps = mSettings.peekPackageLPr(oldName); } // If there was no original package, see one for the real package name. if (ps == null) { - ps = mSettings.peekPackageLP(pkg.packageName); + ps = mSettings.peekPackageLPr(pkg.packageName); } // Check to see if this package could be hiding/updating a system // package. Must look for it either under the original or real @@ -2641,6 +2669,7 @@ class PackageManagerService extends IPackageManager.Stub { // At this point, its safely assumed that package installation for // apps in system partition will go through. If not there won't be a working // version of the app + // writer synchronized (mPackages) { // Just remove the loaded entries from package lists. mPackages.remove(ps.name); @@ -2652,7 +2681,7 @@ class PackageManagerService extends IPackageManager.Stub { InstallArgs args = new FileInstallArgs(ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); args.cleanUpResourcesLI(); - mSettings.enableSystemPackageLP(ps.name); + mSettings.enableSystemPackageLPw(ps.name); } } } @@ -2710,7 +2739,7 @@ class PackageManagerService extends IPackageManager.Stub { PackageParser.Package pkg) { if (pkgSetting.signatures.mSignatures != null) { // Already existing package. Make sure signatures match - if (checkSignaturesLP(pkgSetting.signatures.mSignatures, pkg.mSignatures) != + if (compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { Slog.e(TAG, "Package " + pkg.packageName + " signatures do not match the previously installed version; ignoring!"); @@ -2720,7 +2749,7 @@ class PackageManagerService extends IPackageManager.Stub { } // Check for shared user signatures if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { - if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures, + if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { Slog.e(TAG, "Package " + pkg.packageName + " has no signatures that match those in shared user " @@ -2787,7 +2816,7 @@ class PackageManagerService extends IPackageManager.Stub { return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; } - private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) { + private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName @@ -2861,8 +2890,11 @@ class PackageManagerService extends IPackageManager.Stub { } } - if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) Log.d( - TAG, "Scanning package " + pkg.packageName); + if (DEBUG_PACKAGE_SCANNING) { + if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) + Log.d(TAG, "Scanning package " + pkg.packageName); + } + if (mPackages.containsKey(pkg.packageName) || mSharedLibraries.containsKey(pkg.packageName)) { Slog.w(TAG, "Application package " + pkg.packageName @@ -2885,6 +2917,7 @@ class PackageManagerService extends IPackageManager.Stub { pkg.mAdoptPermissions = null; } + // writer synchronized (mPackages) { // Check all shared libraries and map to their actual file path. if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) { @@ -2895,7 +2928,7 @@ class PackageManagerService extends IPackageManager.Stub { int num = 0; int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0; for (int i=0; i<N; i++) { - String file = mSharedLibraries.get(pkg.usesLibraries.get(i)); + final String file = mSharedLibraries.get(pkg.usesLibraries.get(i)); if (file == null) { Slog.e(TAG, "Package " + pkg.packageName + " requires unavailable shared library " @@ -2908,7 +2941,7 @@ class PackageManagerService extends IPackageManager.Stub { } N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0; for (int i=0; i<N; i++) { - String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i)); + final String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i)); if (file == null) { Slog.w(TAG, "Package " + pkg.packageName + " desires unavailable shared library " @@ -2926,7 +2959,7 @@ class PackageManagerService extends IPackageManager.Stub { } if (pkg.mSharedUserId != null) { - suid = mSettings.getSharedUserLP(pkg.mSharedUserId, + suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, pkg.applicationInfo.flags, true); if (suid == null) { Slog.w(TAG, "Creating application package " + pkg.packageName @@ -2934,18 +2967,10 @@ class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } - if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) { - Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" - + suid.userId + "): packages=" + suid.packages); - } - } - - if (false) { - if (pkg.mOriginalPackages != null) { - Log.w(TAG, "WAITING FOR DEBUGGER"); - Debug.waitForDebugger(); - Log.i(TAG, "Package " + pkg.packageName + " from original packages" - + pkg.mOriginalPackages); + if (DEBUG_PACKAGE_SCANNING) { + if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) + Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId + + "): packages=" + suid.packages); } } @@ -2955,7 +2980,7 @@ class PackageManagerService extends IPackageManager.Stub { if (pkg.mOriginalPackages != null) { // This package may need to be renamed to a previously // installed name. Let's check on that... - String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage); + final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage); if (pkg.mOriginalPackages.contains(renamed)) { // This package had originally been installed as the // original name, and we have already taken care of @@ -2971,11 +2996,11 @@ class PackageManagerService extends IPackageManager.Stub { } else { for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) { - if ((origPackage=mSettings.peekPackageLP( + if ((origPackage = mSettings.peekPackageLPr( pkg.mOriginalPackages.get(i))) != null) { // We do have the package already installed under its // original name... should we use it? - if (!verifyPackageUpdate(origPackage, pkg)) { + if (!verifyPackageUpdateLPr(origPackage, pkg)) { // New package is not compatible with original. origPackage = null; continue; @@ -3006,7 +3031,7 @@ class PackageManagerService extends IPackageManager.Stub { // Just create the setting, don't add it yet. For already existing packages // the PkgSetting exists already and doesn't have to be created. - pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile, + pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.flags, true, false); if (pkgSetting == null) { @@ -3059,7 +3084,7 @@ class PackageManagerService extends IPackageManager.Stub { // associated with an overall shared user, which doesn't seem all // that unreasonable. if (pkgSetting.sharedUser != null) { - if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures, + if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser); mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; @@ -3077,7 +3102,7 @@ class PackageManagerService extends IPackageManager.Stub { // package isn't already installed, since we don't want to break // things that are installed. if ((scanMode&SCAN_NEW_INSTALL) != 0) { - int N = pkg.providers.size(); + final int N = pkg.providers.size(); int i; for (i=0; i<N; i++) { PackageParser.Provider p = pkg.providers.get(i); @@ -3098,29 +3123,28 @@ class PackageManagerService extends IPackageManager.Stub { } } } - } - final String pkgName = pkg.packageName; - - if (pkg.mAdoptPermissions != null) { - // This package wants to adopt ownership of permissions from - // another package. - for (int i=pkg.mAdoptPermissions.size()-1; i>=0; i--) { - String origName = pkg.mAdoptPermissions.get(i); - PackageSetting orig = mSettings.peekPackageLP(origName); - if (orig != null) { - if (verifyPackageUpdate(orig, pkg)) { - Slog.i(TAG, "Adopting permissions from " - + origName + " to " + pkg.packageName); - mSettings.transferPermissions(origName, pkg.packageName); + if (pkg.mAdoptPermissions != null) { + // This package wants to adopt ownership of permissions from + // another package. + for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) { + final String origName = pkg.mAdoptPermissions.get(i); + final PackageSetting orig = mSettings.peekPackageLPr(origName); + if (orig != null) { + if (verifyPackageUpdateLPr(orig, pkg)) { + Slog.i(TAG, "Adopting permissions from " + origName + " to " + + pkg.packageName); + mSettings.transferPermissionsLPw(origName, pkg.packageName); + } } } } } + + final String pkgName = pkg.packageName; final long scanFileTime = scanFile.lastModified(); final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0; - final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.timeStamp; pkg.applicationInfo.processName = fixProcessName( pkg.applicationInfo.packageName, pkg.applicationInfo.processName, @@ -3185,6 +3209,7 @@ class PackageManagerService extends IPackageManager.Stub { + " has mismatched uid: " + mOutPermissions[1] + " on disk, " + pkg.applicationInfo.uid + " in settings"; + // writer synchronized (mPackages) { mSettings.mReadMessages.append(msg); mSettings.mReadMessages.append('\n'); @@ -3197,8 +3222,10 @@ class PackageManagerService extends IPackageManager.Stub { } pkg.applicationInfo.dataDir = dataPath.getPath(); } else { - if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGV) - Log.v(TAG, "Want this data dir: " + dataPath); + if (DEBUG_PACKAGE_SCANNING) { + if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) + Log.v(TAG, "Want this data dir: " + dataPath); + } //invoke installer to do the actual installation if (mInstaller != null) { int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, @@ -3316,13 +3343,14 @@ class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.uid); } + // writer synchronized (mPackages) { // We don't expect installation to fail beyond this point, if ((scanMode&SCAN_MONITOR) != 0) { mAppDirs.put(pkg.mPath, pkg); } // Add the new setting to mSettings - mSettings.insertPackageSettingLP(pkgSetting, pkg); + mSettings.insertPackageSettingLPw(pkgSetting, pkg); // Add the new setting to mPackages mPackages.put(pkg.applicationInfo.packageName, pkg); // Make sure we don't accidentally delete its data. @@ -3378,10 +3406,12 @@ class PackageManagerService extends IPackageManager.Stub { } else { p.info.authority = p.info.authority + ";" + names[j]; } - if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) - Log.d(TAG, "Registered content provider: " + names[j] + - ", className = " + p.info.name + - ", isSyncable = " + p.info.isSyncable); + if (DEBUG_PACKAGE_SCANNING) { + if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) + Log.d(TAG, "Registered content provider: " + names[j] + + ", className = " + p.info.name + ", isSyncable = " + + p.info.isSyncable); + } } else { PackageParser.Provider other = mProviders.get(names[j]); Slog.w(TAG, "Skipping provider name " + names[j] + @@ -3402,7 +3432,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Providers: " + r); + if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Providers: " + r); } N = pkg.services.size(); @@ -3422,7 +3452,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Services: " + r); + if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r); } N = pkg.receivers.size(); @@ -3442,7 +3472,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Receivers: " + r); + if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r); } N = pkg.activities.size(); @@ -3462,7 +3492,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Activities: " + r); + if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r); } N = pkg.permissionGroups.size(); @@ -3496,7 +3526,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Permission Groups: " + r); + if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r); } N = pkg.permissions.size(); @@ -3561,7 +3591,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Permissions: " + r); + if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permissions: " + r); } N = pkg.instrumentation.size(); @@ -3584,7 +3614,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r); + if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r); } if (pkg.protectedBroadcasts != null) { @@ -3613,25 +3643,15 @@ class PackageManagerService extends IPackageManager.Stub { } } - // Return the path of the directory that will contain the native binaries - // of a given installed package. This is relative to the data path. - // - private File getNativeBinaryDirForPackage(PackageParser.Package pkg) { - final String nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir; - if (nativeLibraryDir != null) { - return new File(nativeLibraryDir); - } else { - // Fall back for old packages - return new File(pkg.applicationInfo.dataDir, LIB_DIR_NAME); - } - } - void removePackageLI(PackageParser.Package pkg, boolean chatty) { - if (chatty && Config.LOGD) Log.d( - TAG, "Removing package " + pkg.applicationInfo.packageName ); + if (DEBUG_INSTALL) { + if (chatty) + Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName); + } + // writer synchronized (mPackages) { - clearPackagePreferredActivitiesLP(pkg.packageName); + clearPackagePreferredActivitiesLPw(pkg.packageName); mPackages.remove(pkg.applicationInfo.packageName); if (pkg.mPath != null) { @@ -3657,10 +3677,12 @@ class PackageManagerService extends IPackageManager.Stub { for (int j = 0; j < names.length; j++) { if (mProviders.get(names[j]) == p) { mProviders.remove(names[j]); - if (chatty && Config.LOGD) Log.d( - TAG, "Unregistered content provider: " + names[j] + - ", className = " + p.info.name + - ", isSyncable = " + p.info.isSyncable); + if (DEBUG_REMOVE) { + if (chatty) + Log.d(TAG, "Unregistered content provider: " + names[j] + + ", className = " + p.info.name + ", isSyncable = " + + p.info.isSyncable); + } } } if (chatty) { @@ -3673,7 +3695,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Providers: " + r); + if (DEBUG_REMOVE) Log.d(TAG, " Providers: " + r); } N = pkg.services.size(); @@ -3691,7 +3713,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Services: " + r); + if (DEBUG_REMOVE) Log.d(TAG, " Services: " + r); } N = pkg.receivers.size(); @@ -3709,7 +3731,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Receivers: " + r); + if (DEBUG_REMOVE) Log.d(TAG, " Receivers: " + r); } N = pkg.activities.size(); @@ -3727,17 +3749,15 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Activities: " + r); + if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r); } N = pkg.permissions.size(); r = null; for (i=0; i<N; i++) { PackageParser.Permission p = pkg.permissions.get(i); - boolean tree = false; BasePermission bp = mSettings.mPermissions.get(p.info.name); if (bp == null) { - tree = true; bp = mSettings.mPermissionTrees.get(p.info.name); } if (bp != null && bp.perm == p) { @@ -3753,7 +3773,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Permissions: " + r); + if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); } N = pkg.instrumentation.size(); @@ -3771,7 +3791,7 @@ class PackageManagerService extends IPackageManager.Stub { } } if (r != null) { - if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r); + if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r); } } } @@ -3789,14 +3809,13 @@ class PackageManagerService extends IPackageManager.Stub { return false; } - private void updatePermissionsLP(String changingPkg, + private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, boolean grantPermissions, boolean replace, boolean replaceAll) { // Make sure there are no dangling permission trees. - Iterator<BasePermission> it = mSettings.mPermissionTrees - .values().iterator(); + Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); while (it.hasNext()) { - BasePermission bp = it.next(); + final BasePermission bp = it.next(); if (bp.packageSetting == null) { // We may not yet have parsed the package, so just see if // we still know about its settings. @@ -3820,13 +3839,13 @@ class PackageManagerService extends IPackageManager.Stub { // and make sure there are no dangling permissions. it = mSettings.mPermissions.values().iterator(); while (it.hasNext()) { - BasePermission bp = it.next(); + final BasePermission bp = it.next(); if (bp.type == BasePermission.TYPE_DYNAMIC) { if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" + bp.name + " pkg=" + bp.sourcePackage + " info=" + bp.pendingInfo); if (bp.packageSetting == null && bp.pendingInfo != null) { - BasePermission tree = findPermissionTreeLP(bp.name); + final BasePermission tree = findPermissionTreeLP(bp.name); if (tree != null) { bp.packageSetting = tree.packageSetting; bp.perm = new PackageParser.Permission(tree.perm.owner, @@ -3861,18 +3880,18 @@ class PackageManagerService extends IPackageManager.Stub { if (grantPermissions) { for (PackageParser.Package pkg : mPackages.values()) { if (pkg != pkgInfo) { - grantPermissionsLP(pkg, replaceAll); + grantPermissionsLPw(pkg, replaceAll); } } } if (pkgInfo != null) { - grantPermissionsLP(pkgInfo, replace); + grantPermissionsLPw(pkgInfo, replace); } } - private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) { - final PackageSetting ps = (PackageSetting)pkg.mExtras; + private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) { + final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { return; } @@ -3893,12 +3912,11 @@ class PackageManagerService extends IPackageManager.Stub { final int N = pkg.requestedPermissions.size(); for (int i=0; i<N; i++) { - String name = pkg.requestedPermissions.get(i); - BasePermission bp = mSettings.mPermissions.get(name); - if (false) { + final String name = pkg.requestedPermissions.get(i); + final BasePermission bp = mSettings.mPermissions.get(name); + if (DEBUG_INSTALL) { if (gp != ps) { - Log.i(TAG, "Package " + pkg.packageName + " checking " + name - + ": " + bp); + Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp); } } if (bp != null && bp.packageSetting != null) { @@ -3913,10 +3931,10 @@ class PackageManagerService extends IPackageManager.Stub { allowed = false; } else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE || bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { - allowed = (checkSignaturesLP( + allowed = (compareSignatures( bp.packageSetting.signatures.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH) - || (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures) + || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH); if (!allowed && bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { @@ -3924,8 +3942,8 @@ class PackageManagerService extends IPackageManager.Stub { // For updated system applications, the signatureOrSystem permission // is granted only if it had been defined by the original application. if (isUpdatedSystemApp(pkg)) { - PackageSetting sysPs = mSettings.getDisabledSystemPkg( - pkg.packageName); + final PackageSetting sysPs = mSettings + .getDisabledSystemPkgLPr(pkg.packageName); final GrantedPermissions origGp = sysPs.sharedUser != null ? sysPs.sharedUser : sysPs; if (origGp.grantedPermissions.contains(perm)) { @@ -3944,7 +3962,7 @@ class PackageManagerService extends IPackageManager.Stub { } else { allowed = false; } - if (false) { + if (DEBUG_INSTALL) { if (gp != ps) { Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); } @@ -4022,25 +4040,26 @@ class PackageManagerService extends IPackageManager.Stub { private final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { - public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { + public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, + boolean defaultOnly) { mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; return super.queryIntent(intent, resolvedType, defaultOnly); } - public List queryIntent(Intent intent, String resolvedType, int flags) { + public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags) { mFlags = flags; return super.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); } - public List queryIntentForPackage(Intent intent, String resolvedType, int flags, - ArrayList<PackageParser.Activity> packageActivities) { + public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, + int flags, ArrayList<PackageParser.Activity> packageActivities) { if (packageActivities == null) { return null; } mFlags = flags; final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; - int N = packageActivities.size(); + final int N = packageActivities.size(); ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut = new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N); @@ -4057,11 +4076,13 @@ class PackageManagerService extends IPackageManager.Stub { public final void addActivity(PackageParser.Activity a, String type) { final boolean systemApp = isSystemApp(a.info.applicationInfo); mActivities.put(a.getComponentName(), a); - if (SHOW_INFO || Config.LOGV) Log.v( + if (DEBUG_SHOW_INFO) + Log.v( TAG, " " + type + " " + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); - if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name); - int NI = a.intents.size(); + if (DEBUG_SHOW_INFO) + Log.v(TAG, " Class=" + a.info.name); + final int NI = a.intents.size(); for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) { @@ -4069,7 +4090,7 @@ class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity " + a.className + " with priority > 0, forcing to 0"); } - if (SHOW_INFO || Config.LOGV) { + if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } @@ -4082,14 +4103,16 @@ class PackageManagerService extends IPackageManager.Stub { public final void removeActivity(PackageParser.Activity a, String type) { mActivities.remove(a.getComponentName()); - if (SHOW_INFO || Config.LOGV) Log.v( - TAG, " " + type + " " + - (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); - if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name); - int NI = a.intents.size(); + if (DEBUG_SHOW_INFO) { + Log.v(TAG, " " + type + " " + + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel + : a.info.name) + ":"); + Log.v(TAG, " Class=" + a.info.name); + } + final int NI = a.intents.size(); for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); - if (SHOW_INFO || Config.LOGV) { + if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } @@ -4131,7 +4154,7 @@ class PackageManagerService extends IPackageManager.Stub { @Override protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info, int match) { - if (!mSettings.isEnabledLP(info.activity.info, mFlags)) { + if (!mSettings.isEnabledLPr(info.activity.info, mFlags)) { return null; } final PackageParser.Activity activity = info.activity; @@ -4193,25 +4216,26 @@ class PackageManagerService extends IPackageManager.Stub { private final class ServiceIntentResolver extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> { - public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { + public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, + boolean defaultOnly) { mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; return super.queryIntent(intent, resolvedType, defaultOnly); } - public List queryIntent(Intent intent, String resolvedType, int flags) { + public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags) { mFlags = flags; return super.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); } - public List queryIntentForPackage(Intent intent, String resolvedType, int flags, - ArrayList<PackageParser.Service> packageServices) { + public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, + int flags, ArrayList<PackageParser.Service> packageServices) { if (packageServices == null) { return null; } mFlags = flags; final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; - int N = packageServices.size(); + final int N = packageServices.size(); ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut = new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N); @@ -4227,16 +4251,17 @@ class PackageManagerService extends IPackageManager.Stub { public final void addService(PackageParser.Service s) { mServices.put(s.getComponentName(), s); - if (SHOW_INFO || Config.LOGV) Log.v( - TAG, " " + (s.info.nonLocalizedLabel != null + if (DEBUG_SHOW_INFO) { + Log.v(TAG, " " + + (s.info.nonLocalizedLabel != null ? s.info.nonLocalizedLabel : s.info.name) + ":"); - if (SHOW_INFO || Config.LOGV) Log.v( - TAG, " Class=" + s.info.name); - int NI = s.intents.size(); + Log.v(TAG, " Class=" + s.info.name); + } + final int NI = s.intents.size(); int j; for (j=0; j<NI; j++) { PackageParser.ServiceIntentInfo intent = s.intents.get(j); - if (SHOW_INFO || Config.LOGV) { + if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } @@ -4249,16 +4274,16 @@ class PackageManagerService extends IPackageManager.Stub { public final void removeService(PackageParser.Service s) { mServices.remove(s.getComponentName()); - if (SHOW_INFO || Config.LOGV) Log.v( - TAG, " " + (s.info.nonLocalizedLabel != null + if (DEBUG_SHOW_INFO) { + Log.v(TAG, " " + (s.info.nonLocalizedLabel != null ? s.info.nonLocalizedLabel : s.info.name) + ":"); - if (SHOW_INFO || Config.LOGV) Log.v( - TAG, " Class=" + s.info.name); - int NI = s.intents.size(); + Log.v(TAG, " Class=" + s.info.name); + } + final int NI = s.intents.size(); int j; for (j=0; j<NI; j++) { PackageParser.ServiceIntentInfo intent = s.intents.get(j); - if (SHOW_INFO || Config.LOGV) { + if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } @@ -4301,7 +4326,7 @@ class PackageManagerService extends IPackageManager.Stub { protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter, int match) { final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter; - if (!mSettings.isEnabledLP(info.service.info, mFlags)) { + if (!mSettings.isEnabledLPr(info.service.info, mFlags)) { return null; } final PackageParser.Service service = info.service; @@ -4394,7 +4419,7 @@ class PackageManagerService extends IPackageManager.Stub { } }; - private static final void sendPackageBroadcast(String action, String pkg, + static final void sendPackageBroadcast(String action, String pkg, Bundle extras, String targetPkg, IIntentReceiver finishedReceiver) { IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { @@ -4425,6 +4450,7 @@ class PackageManagerService extends IPackageManager.Stub { } public String nextPackageToClean(String lastPackage) { + // writer synchronized (mPackages) { if (!isExternalMediaAvailable()) { // If the external storage is no longer mounted at this point, @@ -4445,6 +4471,7 @@ class PackageManagerService extends IPackageManager.Stub { } void startCleaningPackages() { + // reader synchronized (mPackages) { if (!isExternalMediaAvailable()) { return; @@ -4477,6 +4504,7 @@ class PackageManagerService extends IPackageManager.Stub { String addedPackage = null; int addedUid = -1; + // TODO post a message to the handler to obtain serial ordering synchronized (mInstallLock) { String fullPathStr = null; File fullPath = null; @@ -4485,13 +4513,12 @@ class PackageManagerService extends IPackageManager.Stub { fullPathStr = fullPath.getPath(); } - if (Config.LOGV) Log.v( - TAG, "File " + fullPathStr + " changed: " - + Integer.toHexString(event)); + if (DEBUG_APP_DIR_OBSERVER) + Log.v(TAG, "File " + fullPathStr + " changed: " + Integer.toHexString(event)); if (!isPackageFilename(path)) { - if (Config.LOGV) Log.v( - TAG, "Ignoring change of non-package file: " + fullPathStr); + if (DEBUG_APP_DIR_OBSERVER) + Log.v(TAG, "Ignoring change of non-package file: " + fullPathStr); return; } @@ -4501,6 +4528,7 @@ class PackageManagerService extends IPackageManager.Stub { return; } PackageParser.Package p = null; + // reader synchronized (mPackages) { p = mAppDirs.get(fullPathStr); } @@ -4522,8 +4550,14 @@ class PackageManagerService extends IPackageManager.Stub { SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME, System.currentTimeMillis()); if (p != null) { + /* + * TODO this seems dangerous as the package may have + * changed since we last acquired the mPackages + * lock. + */ + // writer synchronized (mPackages) { - updatePermissionsLP(p.packageName, p, + updatePermissionsLPw(p.packageName, p, p.permissions.size() > 0, false, false); } addedPackage = p.applicationInfo.packageName; @@ -4532,8 +4566,9 @@ class PackageManagerService extends IPackageManager.Stub { } } + // reader synchronized (mPackages) { - mSettings.writeLP(); + mSettings.writeLPr(); } } @@ -4581,13 +4616,9 @@ class PackageManagerService extends IPackageManager.Stub { mHandler.sendMessage(msg); } - public void setInstallerPackageName(String targetPackage, - String installerPackageName) { - PackageSetting pkgSetting; + public void setInstallerPackageName(String targetPackage, String installerPackageName) { final int uid = Binder.getCallingUid(); - final int permission = mContext.checkCallingPermission( - android.Manifest.permission.INSTALL_PACKAGES); - final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + // writer synchronized (mPackages) { PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage); if (targetPackageSetting == null) { @@ -4606,7 +4637,7 @@ class PackageManagerService extends IPackageManager.Stub { } Signature[] callerSignature; - Object obj = mSettings.getUserIdLP(uid); + Object obj = mSettings.getUserIdLPr(uid); if (obj != null) { if (obj instanceof SharedUserSetting) { callerSignature = ((SharedUserSetting)obj).signatures.mSignatures; @@ -4622,7 +4653,7 @@ class PackageManagerService extends IPackageManager.Stub { // Verify: can't set installerPackageName to a package that is // not signed with the same cert as the caller. if (installerPackageSetting != null) { - if (checkSignaturesLP(callerSignature, + if (compareSignatures(callerSignature, installerPackageSetting.signatures.mSignatures) != PackageManager.SIGNATURE_MATCH) { throw new SecurityException( @@ -4639,7 +4670,7 @@ class PackageManagerService extends IPackageManager.Stub { // If the currently set package isn't valid, then it's always // okay to change it. if (setting != null) { - if (checkSignaturesLP(callerSignature, + if (compareSignatures(callerSignature, setting.signatures.mSignatures) != PackageManager.SIGNATURE_MATCH) { throw new SecurityException( @@ -4855,6 +4886,7 @@ class PackageManagerService extends IPackageManager.Stub { String packageName = pkgLite.packageName; int installLocation = pkgLite.installLocation; boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0; + // reader synchronized (mPackages) { PackageParser.Package pkg = mPackages.get(packageName); if (pkg != null) { @@ -4919,12 +4951,24 @@ class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Cannot install fwd locked apps on sdcard"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { + final long lowThreshold; + + final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager + .getService(DeviceStorageMonitorService.SERVICE); + if (dsm == null) { + Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); + lowThreshold = 0L; + } else { + lowThreshold = dsm.getMemoryLowThreshold(); + } + // Remote call to find out default install location final PackageInfoLite pkgLite; try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags); + pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags, + lowThreshold); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -5144,10 +5188,26 @@ class PackageManagerService extends IPackageManager.Stub { } boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { + final long lowThreshold; + + final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager + .getService(DeviceStorageMonitorService.SERVICE); + if (dsm == null) { + Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); + lowThreshold = 0L; + } else { + if (dsm.isMemoryLow()) { + Log.w(TAG, "Memory is reported as being too low; aborting package install"); + return false; + } + + lowThreshold = dsm.getMemoryLowThreshold(); + } + try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkFreeStorage(false, packageURI); + return imcs.checkInternalFreeStorage(packageURI, lowThreshold); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -5373,7 +5433,7 @@ class PackageManagerService extends IPackageManager.Stub { try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkFreeStorage(true, packageURI); + return imcs.checkExternalFreeStorage(packageURI); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -5697,7 +5757,7 @@ class PackageManagerService extends IPackageManager.Stub { // First find the old package info and check signatures synchronized(mPackages) { oldPackage = mPackages.get(pkgName); - if (checkSignaturesLP(oldPackage.mSignatures, pkg.mSignatures) + if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; return; @@ -5720,11 +5780,6 @@ class PackageManagerService extends IPackageManager.Stub { boolean deletedPkg = true; boolean updatedSettings = false; - String oldInstallerPackageName = null; - synchronized (mPackages) { - oldInstallerPackageName = mSettings.getInstallerPackageName(pkgName); - } - long origUpdateTime; if (pkg.mExtras != null) { origUpdateTime = ((PackageSetting)pkg.mExtras).lastUpdateTime; @@ -5788,10 +5843,12 @@ class PackageManagerService extends IPackageManager.Stub { return; } // Restore of old package succeeded. Update permissions. + // writer synchronized (mPackages) { - updatePermissionsLP(deletedPackage.packageName, deletedPackage, + updatePermissionsLPw(deletedPackage.packageName, deletedPackage, true, false, false); - mSettings.writeLP(); + // can downgrade to reader + mSettings.writeLPr(); } Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade"); } @@ -5814,6 +5871,7 @@ class PackageManagerService extends IPackageManager.Stub { } PackageParser.Package oldPkg; PackageSetting oldPkgSetting; + // reader synchronized (mPackages) { oldPkg = mPackages.get(packageName); oldPkgSetting = mSettings.mPackages.get(packageName); @@ -5830,8 +5888,9 @@ class PackageManagerService extends IPackageManager.Stub { res.removedInfo.removedPackage = packageName; // Remove existing system package removePackageLI(oldPkg, true); + // writer synchronized (mPackages) { - if (!mSettings.disableSystemPackageLP(packageName) && deletedPackage != null) { + if (!mSettings.disableSystemPackageLPw(packageName) && deletedPackage != null) { // We didn't need to disable the .apk as a current system package, // which means we are replacing another update that is already // installed. We need to make sure to delete the older one's .apk. @@ -5875,11 +5934,11 @@ class PackageManagerService extends IPackageManager.Stub { // Restore the old system information in Settings synchronized(mPackages) { if (updatedSettings) { - mSettings.enableSystemPackageLP(packageName); + mSettings.enableSystemPackageLPw(packageName); mSettings.setInstallerPackageName(packageName, oldPkgSetting.installerPackageName); } - mSettings.writeLP(); + mSettings.writeLPr(); } } } @@ -5913,8 +5972,8 @@ class PackageManagerService extends IPackageManager.Stub { //write settings. the installStatus will be incomplete at this stage. //note that the new package setting would have already been //added to mPackages. It hasn't been persisted yet. - mSettings.setInstallStatus(pkgName, PKG_INSTALL_INCOMPLETE); - mSettings.writeLP(); + mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE); + mSettings.writeLPr(); } if ((res.returnCode = moveDexFilesLI(newPackage)) @@ -5932,16 +5991,16 @@ class PackageManagerService extends IPackageManager.Stub { Log.d(TAG, "New package installed in " + newPackage.mPath); } synchronized (mPackages) { - updatePermissionsLP(newPackage.packageName, newPackage, + updatePermissionsLPw(newPackage.packageName, newPackage, newPackage.permissions.size() > 0, true, false); res.name = pkgName; res.uid = newPackage.applicationInfo.uid; res.pkg = newPackage; - mSettings.setInstallStatus(pkgName, PKG_INSTALL_COMPLETE); + mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE); mSettings.setInstallerPackageName(pkgName, installerPackageName); res.returnCode = PackageManager.INSTALL_SUCCEEDED; //to update install status - mSettings.writeLP(); + mSettings.writeLPr(); } } @@ -6040,7 +6099,6 @@ class PackageManagerService extends IPackageManager.Stub { } private int setPermissionsLI(PackageParser.Package newPackage) { - String pkgName = newPackage.packageName; int retCode = 0; // TODO Gross hack but fix later. Ideally move this to be a post installation // check after alloting uid. @@ -6070,9 +6128,8 @@ class PackageManagerService extends IPackageManager.Stub { } if (retCode != 0) { - Slog.e(TAG, "Couldn't set new package file permissions for " + - newPackage.mPath - + ". The return code was: " + retCode); + Slog.e(TAG, "Couldn't set new package file permissions for " + newPackage.mPath + + ". The return code was: " + retCode); // TODO Define new internal error return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } @@ -6321,7 +6378,8 @@ class PackageManagerService extends IPackageManager.Stub { } removePackageLI(p, (flags&REMOVE_CHATTY) != 0); // Retrieve object to delete permissions for shared user later on - PackageSetting deletedPs; + final PackageSetting deletedPs; + // reader synchronized (mPackages) { deletedPs = mSettings.mPackages.get(packageName); } @@ -6335,23 +6393,28 @@ class PackageManagerService extends IPackageManager.Stub { } } else { // for simulator - PackageParser.Package pkg = mPackages.get(packageName); - File dataDir = new File(pkg.applicationInfo.dataDir); + File dataDir; + // reader + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(packageName); + dataDir = new File(pkg.applicationInfo.dataDir); + } dataDir.delete(); } schedulePackageCleaning(packageName); } + // writer synchronized (mPackages) { if (deletedPs != null) { if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { if (outInfo != null) { - outInfo.removedUid = mSettings.removePackageLP(packageName); + outInfo.removedUid = mSettings.removePackageLPw(packageName); } if (deletedPs != null) { - updatePermissionsLP(deletedPs.name, null, false, false, false); + updatePermissionsLPw(deletedPs.name, null, false, false, false); if (deletedPs.sharedUser != null) { // remove permissions associated with package - mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids); + mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids); } } } @@ -6366,9 +6429,10 @@ class PackageManagerService extends IPackageManager.Stub { mSettings.mPreferredActivities.removeFilter(pa); } } + // can downgrade to reader if (writeSettings) { // Save settings now - mSettings.writeLP(); + mSettings.writeLPr(); } } } @@ -6388,8 +6452,9 @@ class PackageManagerService extends IPackageManager.Stub { // Confirm if the system package has been updated // An updated system app can be deleted. This will also have to restore // the system pkg from system partition + // reader synchronized (mPackages) { - ps = mSettings.getDisabledSystemPkg(p.packageName); + ps = mSettings.getDisabledSystemPkgLPr(p.packageName); } if (ps == null) { Slog.w(TAG, "Attempt to delete unknown system package "+ p.packageName); @@ -6411,9 +6476,10 @@ class PackageManagerService extends IPackageManager.Stub { if (!ret) { return false; } + // writer synchronized (mPackages) { // Reinstate the old system package - mSettings.enableSystemPackageLP(p.packageName); + mSettings.enableSystemPackageLPw(p.packageName); // Remove any native libraries from the upgraded package. NativeLibraryHelper.removeNativeBinariesLI(p.applicationInfo.nativeLibraryDir); } @@ -6426,10 +6492,12 @@ class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError); return false; } + // writer synchronized (mPackages) { - updatePermissionsLP(newPkg.packageName, newPkg, true, true, false); + updatePermissionsLPw(newPkg.packageName, newPkg, true, true, false); + // can downgrade to reader here if (writeSettings) { - mSettings.writeLP(); + mSettings.writeLPr(); } } return true; @@ -6717,16 +6785,14 @@ class PackageManagerService extends IPackageManager.Stub { return new ArrayList<PackageInfo>(); } - int getUidTargetSdkVersionLockedLP(int uid) { - Object obj = mSettings.getUserIdLP(uid); + private int getUidTargetSdkVersionLockedLPr(int uid) { + Object obj = mSettings.getUserIdLPr(uid); if (obj instanceof SharedUserSetting) { - SharedUserSetting sus = (SharedUserSetting)obj; - final int N = sus.packages.size(); + final SharedUserSetting sus = (SharedUserSetting) obj; int vers = Build.VERSION_CODES.CUR_DEVELOPMENT; - Iterator<PackageSetting> it = sus.packages.iterator(); - int i=0; + final Iterator<PackageSetting> it = sus.packages.iterator(); while (it.hasNext()) { - PackageSetting ps = it.next(); + final PackageSetting ps = it.next(); if (ps.pkg != null) { int v = ps.pkg.applicationInfo.targetSdkVersion; if (v < vers) vers = v; @@ -6734,7 +6800,7 @@ class PackageManagerService extends IPackageManager.Stub { } return vers; } else if (obj instanceof PackageSetting) { - PackageSetting ps = (PackageSetting)obj; + final PackageSetting ps = (PackageSetting) obj; if (ps.pkg != null) { return ps.pkg.applicationInfo.targetSdkVersion; } @@ -6744,11 +6810,12 @@ class PackageManagerService extends IPackageManager.Stub { public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { + // writer synchronized (mPackages) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid()) + if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid()) < Build.VERSION_CODES.FROYO) { Slog.w(TAG, "Ignoring addPreferredActivity() from uid " + Binder.getCallingUid()); @@ -6788,7 +6855,7 @@ class PackageManagerService extends IPackageManager.Stub { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid()) + if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid()) < Build.VERSION_CODES.FROYO) { Slog.w(TAG, "Ignoring replacePreferredActivity() from uid " + Binder.getCallingUid()); @@ -6814,14 +6881,15 @@ class PackageManagerService extends IPackageManager.Stub { } public void clearPackagePreferredActivities(String packageName) { + final int uid = Binder.getCallingUid(); + // writer synchronized (mPackages) { - int uid = Binder.getCallingUid(); PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null || pkg.applicationInfo.uid != uid) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid()) + if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid()) < Build.VERSION_CODES.FROYO) { Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid " + Binder.getCallingUid()); @@ -6832,13 +6900,13 @@ class PackageManagerService extends IPackageManager.Stub { } } - if (clearPackagePreferredActivitiesLP(packageName)) { + if (clearPackagePreferredActivitiesLPw(packageName)) { scheduleWriteSettingsLocked(); } } } - boolean clearPackagePreferredActivitiesLP(String packageName) { + boolean clearPackagePreferredActivitiesLPw(String packageName) { boolean changed = false; Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); while (it.hasNext()) { @@ -6855,10 +6923,11 @@ class PackageManagerService extends IPackageManager.Stub { List<ComponentName> outActivities, String packageName) { int num = 0; + // reader synchronized (mPackages) { - Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); + final Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); while (it.hasNext()) { - PreferredActivity pa = it.next(); + final PreferredActivity pa = it.next(); if (packageName == null || pa.mPref.mComponent.getPackageName().equals(packageName)) { if (outFilters != null) { @@ -6903,6 +6972,8 @@ class PackageManagerService extends IPackageManager.Stub { String componentName = isApp ? packageName : className; int packageUid = -1; ArrayList<String> components; + + // writer synchronized (mPackages) { pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null) { @@ -6932,17 +7003,17 @@ class PackageManagerService extends IPackageManager.Stub { // We're dealing with a component level state change switch (newState) { case COMPONENT_ENABLED_STATE_ENABLED: - if (!pkgSetting.enableComponentLP(className)) { + if (!pkgSetting.enableComponentLPw(className)) { return; } break; case COMPONENT_ENABLED_STATE_DISABLED: - if (!pkgSetting.disableComponentLP(className)) { + if (!pkgSetting.disableComponentLPw(className)) { return; } break; case COMPONENT_ENABLED_STATE_DEFAULT: - if (!pkgSetting.restoreComponentLP(className)) { + if (!pkgSetting.restoreComponentLPw(className)) { return; } break; @@ -6951,10 +7022,10 @@ class PackageManagerService extends IPackageManager.Stub { return; } } - mSettings.writeLP(); + mSettings.writeLPr(); packageUid = pkgSetting.userId; components = mPendingBroadcasts.get(packageName); - boolean newPackage = components == null; + final boolean newPackage = components == null; if (newPackage) { components = new ArrayList<String>(); } @@ -6990,8 +7061,9 @@ class PackageManagerService extends IPackageManager.Stub { private void sendPackageChangedBroadcast(String packageName, boolean killFlag, ArrayList<String> componentNames, int packageUid) { - if (false) Log.v(TAG, "Sending package changed: package=" + packageName - + " components=" + componentNames); + if (DEBUG_INSTALL) + Log.v(TAG, "Sending package changed: package=" + packageName + " components=" + + componentNames); Bundle extras = new Bundle(4); extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0)); String nameList[] = new String[componentNames.size()]; @@ -7003,72 +7075,37 @@ class PackageManagerService extends IPackageManager.Stub { } public void setPackageStoppedState(String packageName, boolean stopped) { - PackageSetting pkgSetting; final int uid = Binder.getCallingUid(); final int permission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + // writer synchronized (mPackages) { - pkgSetting = mSettings.mPackages.get(packageName); - if (pkgSetting == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - if (!allowedByPermission && (uid != pkgSetting.userId)) { - throw new SecurityException( - "Permission Denial: attempt to change stopped state from pid=" - + Binder.getCallingPid() - + ", uid=" + uid + ", package uid=" + pkgSetting.userId); - } - if (DEBUG_STOPPED && stopped) { - RuntimeException e = new RuntimeException("here"); - e.fillInStackTrace(); - Slog.i(TAG, "Stopping package " + packageName, e); - } - if (pkgSetting.stopped != stopped) { - pkgSetting.stopped = stopped; - pkgSetting.pkg.mSetStopped = stopped; - if (pkgSetting.notLaunched) { - if (pkgSetting.installerPackageName != null) { - sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, - pkgSetting.name, null, - pkgSetting.installerPackageName, null); - } - pkgSetting.notLaunched = false; - } + if (mSettings.setPackageStoppedStateLPw(packageName, stopped, allowedByPermission, + uid)) { scheduleWriteStoppedPackagesLocked(); } } } public String getInstallerPackageName(String packageName) { + // reader synchronized (mPackages) { - PackageSetting pkg = mSettings.mPackages.get(packageName); - if (pkg == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - return pkg.installerPackageName; + return mSettings.getInstallerPackageNameLPr(packageName); } } - public int getApplicationEnabledSetting(String appPackageName) { + public int getApplicationEnabledSetting(String packageName) { + // reader synchronized (mPackages) { - PackageSetting pkg = mSettings.mPackages.get(appPackageName); - if (pkg == null) { - throw new IllegalArgumentException("Unknown package: " + appPackageName); - } - return pkg.enabled; + return mSettings.getApplicationEnabledSettingLPr(packageName); } } public int getComponentEnabledSetting(ComponentName componentName) { + // reader synchronized (mPackages) { - final String packageNameStr = componentName.getPackageName(); - PackageSetting pkg = mSettings.mPackages.get(packageNameStr); - if (pkg == null) { - throw new IllegalArgumentException("Unknown component: " + componentName); - } - final String classNameStr = componentName.getClassName(); - return pkg.currentEnabledStateLP(classNameStr); + return mSettings.getComponentEnabledSettingLPr(componentName); } } @@ -7112,6 +7149,76 @@ class PackageManagerService extends IPackageManager.Stub { return buf.toString(); } + static class DumpState { + public static final int DUMP_LIBS = 1 << 0; + + public static final int DUMP_FEATURES = 1 << 1; + + public static final int DUMP_RESOLVERS = 1 << 2; + + public static final int DUMP_PERMISSIONS = 1 << 3; + + public static final int DUMP_PACKAGES = 1 << 4; + + public static final int DUMP_SHARED_USERS = 1 << 5; + + public static final int DUMP_MESSAGES = 1 << 6; + + public static final int DUMP_PROVIDERS = 1 << 7; + + public static final int OPTION_SHOW_FILTERS = 1 << 0; + + private int mTypes; + + private int mOptions; + + private boolean mTitlePrinted; + + private SharedUserSetting mSharedUser; + + public boolean isDumping(int type) { + if (mTypes == 0) { + return true; + } + + return (mTypes & type) != 0; + } + + public void setDump(int type) { + mTypes |= type; + } + + public boolean isOptionEnabled(int option) { + return (mOptions & option) != 0; + } + + public void setOptionEnabled(int option) { + mOptions |= option; + } + + public boolean onTitlePrinted() { + final boolean printed = mTitlePrinted; + mTitlePrinted = true; + return printed; + } + + public boolean getTitlePrinted() { + return mTitlePrinted; + } + + public void setTitlePrinted(boolean enabled) { + mTitlePrinted = enabled; + } + + public SharedUserSetting getSharedUser() { + return mSharedUser; + } + + public void setSharedUser(SharedUserSetting user) { + mSharedUser = user; + } + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -7124,18 +7231,9 @@ class PackageManagerService extends IPackageManager.Stub { return; } - boolean dumpStar = true; - boolean dumpLibs = false; - boolean dumpFeatures = false; - boolean dumpResolvers = false; - boolean dumpPermissions = false; - boolean dumpPackages = false; - boolean dumpSharedUsers = false; - boolean dumpMessages = false; - boolean dumpProviders = false; + DumpState dumpState = new DumpState(); String packageName = null; - boolean showFilters = false; int opti = 0; while (opti < args.length) { @@ -7163,7 +7261,7 @@ class PackageManagerService extends IPackageManager.Stub { pw.println(" <package.name>: info about given package"); return; } else if ("-f".equals(opt)) { - showFilters = true; + dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS); } else { pw.println("Unknown argument: " + opt + "; use -h for help"); } @@ -7177,40 +7275,31 @@ class PackageManagerService extends IPackageManager.Stub { if ("android".equals(cmd) || cmd.contains(".")) { packageName = cmd; } else if ("l".equals(cmd) || "libraries".equals(cmd)) { - dumpStar = false; - dumpLibs = true; + dumpState.setDump(DumpState.DUMP_LIBS); } else if ("f".equals(cmd) || "features".equals(cmd)) { - dumpStar = false; - dumpFeatures = true; + dumpState.setDump(DumpState.DUMP_FEATURES); } else if ("r".equals(cmd) || "resolvers".equals(cmd)) { - dumpStar = false; - dumpResolvers = true; + dumpState.setDump(DumpState.DUMP_RESOLVERS); } else if ("perm".equals(cmd) || "permissions".equals(cmd)) { - dumpStar = false; - dumpPermissions = true; + dumpState.setDump(DumpState.DUMP_PERMISSIONS); } else if ("p".equals(cmd) || "packages".equals(cmd)) { - dumpStar = false; - dumpPackages = true; + dumpState.setDump(DumpState.DUMP_PACKAGES); } else if ("s".equals(cmd) || "shared-users".equals(cmd)) { - dumpStar = false; - dumpSharedUsers = true; + dumpState.setDump(DumpState.DUMP_SHARED_USERS); } else if ("prov".equals(cmd) || "providers".equals(cmd)) { - dumpStar = false; - dumpProviders = true; + dumpState.setDump(DumpState.DUMP_PROVIDERS); } else if ("m".equals(cmd) || "messages".equals(cmd)) { - dumpStar = false; - dumpMessages = true; + dumpState.setDump(DumpState.DUMP_MESSAGES); } } - - boolean printedTitle = false; - + + // reader synchronized (mPackages) { - if ((dumpStar || dumpLibs) && packageName == null) { - if (printedTitle) pw.println(" "); - printedTitle = true; + if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) { + if (dumpState.onTitlePrinted()) + pw.println(" "); pw.println("Libraries:"); - Iterator<String> it = mSharedLibraries.keySet().iterator(); + final Iterator<String> it = mSharedLibraries.keySet().iterator(); while (it.hasNext()) { String name = it.next(); pw.print(" "); @@ -7220,9 +7309,9 @@ class PackageManagerService extends IPackageManager.Stub { } } - if ((dumpStar || dumpFeatures) && packageName == null) { - if (printedTitle) pw.println(" "); - printedTitle = true; + if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) { + if (dumpState.onTitlePrinted()) + pw.println(" "); pw.println("Features:"); Iterator<String> it = mAvailableFeatures.keySet().iterator(); while (it.hasNext()) { @@ -7232,276 +7321,72 @@ class PackageManagerService extends IPackageManager.Stub { } } - if (dumpStar || dumpResolvers) { - if (mActivities.dump(pw, printedTitle - ? "\nActivity Resolver Table:" : "Activity Resolver Table:", - " ", packageName, showFilters)) { - printedTitle = true; + if (dumpState.isDumping(DumpState.DUMP_RESOLVERS)) { + if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:" + : "Activity Resolver Table:", " ", packageName, + dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { + dumpState.setTitlePrinted(true); } - if (mReceivers.dump(pw, printedTitle - ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:", - " ", packageName, showFilters)) { - printedTitle = true; + if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:" + : "Receiver Resolver Table:", " ", packageName, + dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { + dumpState.setTitlePrinted(true); } - if (mServices.dump(pw, printedTitle - ? "\nService Resolver Table:" : "Service Resolver Table:", - " ", packageName, showFilters)) { - printedTitle = true; + if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:" + : "Service Resolver Table:", " ", packageName, + dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { + dumpState.setTitlePrinted(true); } - if (mSettings.mPreferredActivities.dump(pw, printedTitle - ? "\nPreferred Activities:" : "Preferred Activities:", - " ", packageName, showFilters)) { - printedTitle = true; + if (mSettings.mPreferredActivities.dump(pw, + dumpState.getTitlePrinted() ? "\nPreferred Activities:" + : "Preferred Activities:", " ", + packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) { + dumpState.setTitlePrinted(true); } } - boolean printedSomething = false; - if (dumpStar || dumpPermissions) { - for (BasePermission p : mSettings.mPermissions.values()) { - if (packageName != null && !packageName.equals(p.sourcePackage)) { - continue; - } - if (!printedSomething) { - if (printedTitle) pw.println(" "); - pw.println("Permissions:"); - printedSomething = true; - printedTitle = true; - } - pw.print(" Permission ["); pw.print(p.name); pw.print("] ("); - pw.print(Integer.toHexString(System.identityHashCode(p))); - pw.println("):"); - pw.print(" sourcePackage="); pw.println(p.sourcePackage); - pw.print(" uid="); pw.print(p.uid); - pw.print(" gids="); pw.print(arrayToString(p.gids)); - pw.print(" type="); pw.print(p.type); - pw.print(" prot="); pw.println(p.protectionLevel); - if (p.packageSetting != null) { - pw.print(" packageSetting="); pw.println(p.packageSetting); - } - if (p.perm != null) { - pw.print(" perm="); pw.println(p.perm); - } - } + if (dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { + mSettings.dumpPermissionsLPr(pw, packageName, dumpState); } - if (dumpStar || dumpProviders) { - printedSomething = false; + if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { + boolean printedSomething = false; for (PackageParser.Provider p : mProviders.values()) { if (packageName != null && !packageName.equals(p.info.packageName)) { continue; } if (!printedSomething) { - if (printedTitle) pw.println(" "); + if (dumpState.onTitlePrinted()) + pw.println(" "); pw.println("Registered ContentProviders:"); printedSomething = true; - printedTitle = true; } pw.print(" ["); pw.print(p.info.authority); pw.print("]: "); pw.println(p.toString()); } } - printedSomething = false; - SharedUserSetting packageSharedUser = null; - if (dumpStar || dumpPackages) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - Date date = new Date(); - for (PackageSetting ps : mSettings.mPackages.values()) { - if (packageName != null && !packageName.equals(ps.realName) - && !packageName.equals(ps.name)) { - continue; - } - if (!printedSomething) { - if (printedTitle) pw.println(" "); - pw.println("Packages:"); - printedSomething = true; - printedTitle = true; - } - packageSharedUser = ps.sharedUser; - pw.print(" Package ["); - pw.print(ps.realName != null ? ps.realName : ps.name); - pw.print("] ("); - pw.print(Integer.toHexString(System.identityHashCode(ps))); - pw.println("):"); - if (ps.realName != null) { - pw.print(" compat name="); pw.println(ps.name); - } - pw.print(" userId="); pw.print(ps.userId); - pw.print(" gids="); pw.println(arrayToString(ps.gids)); - pw.print(" sharedUser="); pw.println(ps.sharedUser); - pw.print(" pkg="); pw.println(ps.pkg); - pw.print(" codePath="); pw.println(ps.codePathString); - pw.print(" resourcePath="); pw.println(ps.resourcePathString); - pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString); - pw.print(" versionCode="); pw.println(ps.versionCode); - if (ps.pkg != null) { - pw.print(" versionName="); pw.println(ps.pkg.mVersionName); - pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir); - pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion); - if (ps.pkg.mOperationPending) { - pw.println(" mOperationPending=true"); - } - pw.print(" supportsScreens=["); - boolean first = true; - if ((ps.pkg.applicationInfo.flags & - ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) { - if (!first) pw.print(", "); - first = false; - pw.print("small"); - } - if ((ps.pkg.applicationInfo.flags & - ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) { - if (!first) pw.print(", "); - first = false; - pw.print("medium"); - } - if ((ps.pkg.applicationInfo.flags & - ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { - if (!first) pw.print(", "); - first = false; - pw.print("large"); - } - if ((ps.pkg.applicationInfo.flags & - ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) { - if (!first) pw.print(", "); - first = false; - pw.print("xlarge"); - } - if ((ps.pkg.applicationInfo.flags & - ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) { - if (!first) pw.print(", "); - first = false; - pw.print("resizeable"); - } - if ((ps.pkg.applicationInfo.flags & - ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) { - if (!first) pw.print(", "); - first = false; - pw.print("anyDensity"); - } - } - pw.println("]"); - pw.print(" timeStamp="); - date.setTime(ps.timeStamp); pw.println(sdf.format(date)); - pw.print(" firstInstallTime="); - date.setTime(ps.firstInstallTime); pw.println(sdf.format(date)); - pw.print(" lastUpdateTime="); - date.setTime(ps.lastUpdateTime); pw.println(sdf.format(date)); - if (ps.installerPackageName != null) { - pw.print(" installerPackageName="); pw.println(ps.installerPackageName); - } - pw.print(" signatures="); pw.println(ps.signatures); - pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); - pw.print(" haveGids="); pw.println(ps.haveGids); - pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags)); - pw.print(" installStatus="); pw.print(ps.installStatus); - pw.print(" stopped="); pw.print(ps.stopped); - pw.print(" enabled="); pw.println(ps.enabled); - if (ps.disabledComponents.size() > 0) { - pw.println(" disabledComponents:"); - for (String s : ps.disabledComponents) { - pw.print(" "); pw.println(s); - } - } - if (ps.enabledComponents.size() > 0) { - pw.println(" enabledComponents:"); - for (String s : ps.enabledComponents) { - pw.print(" "); pw.println(s); - } - } - if (ps.grantedPermissions.size() > 0) { - pw.println(" grantedPermissions:"); - for (String s : ps.grantedPermissions) { - pw.print(" "); pw.println(s); - } - } - } + if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) { + mSettings.dumpPackagesLPr(pw, packageName, dumpState); } - printedSomething = false; - if (dumpStar || dumpPackages) { - if (mSettings.mRenamedPackages.size() > 0) { - for (HashMap.Entry<String, String> e - : mSettings.mRenamedPackages.entrySet()) { - if (packageName != null && !packageName.equals(e.getKey()) - && !packageName.equals(e.getValue())) { - continue; - } - if (!printedSomething) { - if (printedTitle) pw.println(" "); - pw.println("Renamed packages:"); - printedSomething = true; - printedTitle = true; - } - pw.print(" "); pw.print(e.getKey()); pw.print(" -> "); - pw.println(e.getValue()); - } - } - printedSomething = false; - if (mSettings.mDisabledSysPackages.size() > 0) { - for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) { - if (packageName != null && !packageName.equals(ps.realName) - && !packageName.equals(ps.name)) { - continue; - } - if (!printedSomething) { - if (printedTitle) pw.println(" "); - pw.println("Hidden system packages:"); - printedSomething = true; - printedTitle = true; - } - pw.print(" Package ["); - pw.print(ps.realName != null ? ps.realName : ps.name); - pw.print("] ("); - pw.print(Integer.toHexString(System.identityHashCode(ps))); - pw.println("):"); - if (ps.realName != null) { - pw.print(" compat name="); pw.println(ps.name); - } - pw.print(" userId="); pw.println(ps.userId); - pw.print(" sharedUser="); pw.println(ps.sharedUser); - pw.print(" codePath="); pw.println(ps.codePathString); - pw.print(" resourcePath="); pw.println(ps.resourcePathString); - } - } - } - printedSomething = false; - if (dumpStar || dumpSharedUsers) { - for (SharedUserSetting su : mSettings.mSharedUsers.values()) { - if (packageName != null && su != packageSharedUser) { - continue; - } - if (!printedSomething) { - if (printedTitle) pw.println(" "); - pw.println("Shared users:"); - printedSomething = true; - printedTitle = true; - } - pw.print(" SharedUser ["); pw.print(su.name); pw.print("] ("); - pw.print(Integer.toHexString(System.identityHashCode(su))); - pw.println("):"); - pw.print(" userId="); pw.print(su.userId); - pw.print(" gids="); pw.println(arrayToString(su.gids)); - pw.println(" grantedPermissions:"); - for (String s : su.grantedPermissions) { - pw.print(" "); pw.println(s); - } - } + + if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) { + mSettings.dumpSharedUsersLPr(pw, packageName, dumpState); } - - if ((dumpStar || dumpMessages) && packageName == null) { - if (printedTitle) pw.println(" "); - printedTitle = true; - pw.println("Settings parse messages:"); - pw.print(mSettings.mReadMessages.toString()); - + + if (dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) { + if (dumpState.onTitlePrinted()) + pw.println(" "); + mSettings.dumpReadMessagesLPr(pw, dumpState); + pw.println(" "); pw.println("Package warning messages:"); - File fname = getSettingsProblemFile(); + final File fname = getSettingsProblemFile(); FileInputStream in = null; try { in = new FileInputStream(fname); - int avail = in.available(); - byte[] data = new byte[avail]; + final int avail = in.available(); + final byte[] data = new byte[avail]; in.read(data); pw.print(new String(data)); } catch (FileNotFoundException e) { @@ -7510,7 +7395,7 @@ class PackageManagerService extends IPackageManager.Stub { if (in != null) { try { in.close(); - } catch (IOException e) { + } catch (IOException e) { } } } @@ -7518,2904 +7403,505 @@ class PackageManagerService extends IPackageManager.Stub { } } - static final class BasePermission { - final static int TYPE_NORMAL = 0; - final static int TYPE_BUILTIN = 1; - final static int TYPE_DYNAMIC = 2; - - final String name; - String sourcePackage; - PackageSettingBase packageSetting; - final int type; - int protectionLevel; - PackageParser.Permission perm; - PermissionInfo pendingInfo; - int uid; - int[] gids; - - BasePermission(String _name, String _sourcePackage, int _type) { - name = _name; - sourcePackage = _sourcePackage; - type = _type; - // Default to most conservative protection level. - protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; - } - - public String toString() { - return "BasePermission{" - + Integer.toHexString(System.identityHashCode(this)) - + " " + name + "}"; - } - } - - static class PackageSignatures { - private Signature[] mSignatures; - - PackageSignatures(PackageSignatures orig) { - if (orig != null && orig.mSignatures != null) { - mSignatures = orig.mSignatures.clone(); - } - } - - PackageSignatures(Signature[] sigs) { - assignSignatures(sigs); - } - - PackageSignatures() { - } - - void writeXml(XmlSerializer serializer, String tagName, - ArrayList<Signature> pastSignatures) throws IOException { - if (mSignatures == null) { - return; - } - serializer.startTag(null, tagName); - serializer.attribute(null, "count", - Integer.toString(mSignatures.length)); - for (int i=0; i<mSignatures.length; i++) { - serializer.startTag(null, "cert"); - final Signature sig = mSignatures[i]; - final int sigHash = sig.hashCode(); - final int numPast = pastSignatures.size(); - int j; - for (j=0; j<numPast; j++) { - Signature pastSig = pastSignatures.get(j); - if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) { - serializer.attribute(null, "index", Integer.toString(j)); - break; - } - } - if (j >= numPast) { - pastSignatures.add(sig); - serializer.attribute(null, "index", Integer.toString(numPast)); - serializer.attribute(null, "key", sig.toCharsString()); - } - serializer.endTag(null, "cert"); - } - serializer.endTag(null, tagName); - } - - void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures) - throws IOException, XmlPullParserException { - String countStr = parser.getAttributeValue(null, "count"); - if (countStr == null) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <signatures> has" - + " no count at " + parser.getPositionDescription()); - XmlUtils.skipCurrentTag(parser); - } - final int count = Integer.parseInt(countStr); - mSignatures = new Signature[count]; - int pos = 0; - - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals("cert")) { - if (pos < count) { - String index = parser.getAttributeValue(null, "index"); - if (index != null) { - try { - int idx = Integer.parseInt(index); - String key = parser.getAttributeValue(null, "key"); - if (key == null) { - if (idx >= 0 && idx < pastSignatures.size()) { - Signature sig = pastSignatures.get(idx); - if (sig != null) { - mSignatures[pos] = pastSignatures.get(idx); - pos++; - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <cert> " - + "index " + index + " is not defined at " - + parser.getPositionDescription()); - } - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <cert> " - + "index " + index + " is out of bounds at " - + parser.getPositionDescription()); - } - } else { - while (pastSignatures.size() <= idx) { - pastSignatures.add(null); - } - Signature sig = new Signature(key); - pastSignatures.set(idx, sig); - mSignatures[pos] = sig; - pos++; - } - } catch (NumberFormatException e) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <cert> " - + "index " + index + " is not a number at " - + parser.getPositionDescription()); - } - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <cert> has" - + " no index at " + parser.getPositionDescription()); - } - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: too " - + "many <cert> tags, expected " + count - + " at " + parser.getPositionDescription()); - } - } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <cert>: " - + parser.getName()); - } - XmlUtils.skipCurrentTag(parser); - } - - if (pos < count) { - // Should never happen -- there is an error in the written - // settings -- but if it does we don't want to generate - // a bad array. - Signature[] newSigs = new Signature[pos]; - System.arraycopy(mSignatures, 0, newSigs, 0, pos); - mSignatures = newSigs; - } - } - - private void assignSignatures(Signature[] sigs) { - if (sigs == null) { - mSignatures = null; - return; - } - mSignatures = new Signature[sigs.length]; - for (int i=0; i<sigs.length; i++) { - mSignatures[i] = sigs[i]; - } - } - - @Override - public String toString() { - StringBuffer buf = new StringBuffer(128); - buf.append("PackageSignatures{"); - buf.append(Integer.toHexString(System.identityHashCode(this))); - buf.append(" ["); - if (mSignatures != null) { - for (int i=0; i<mSignatures.length; i++) { - if (i > 0) buf.append(", "); - buf.append(Integer.toHexString( - System.identityHashCode(mSignatures[i]))); - } - } - buf.append("]}"); - return buf.toString(); - } - } - - static class PreferredActivity extends IntentFilter implements PreferredComponent.Callbacks { - final PreferredComponent mPref; - - PreferredActivity(IntentFilter filter, int match, ComponentName[] set, - ComponentName activity) { - super(filter); - mPref = new PreferredComponent(this, match, set, activity); - } - - PreferredActivity(XmlPullParser parser) throws XmlPullParserException, - IOException { - mPref = new PreferredComponent(this, parser); - } - - public void writeToXml(XmlSerializer serializer) throws IOException { - mPref.writeToXml(serializer); - serializer.startTag(null, "filter"); - super.writeToXml(serializer); - serializer.endTag(null, "filter"); - } - - public boolean onReadTag(String tagName, XmlPullParser parser) - throws XmlPullParserException, IOException { - if (tagName.equals("filter")) { - //Log.i(TAG, "Starting to parse filter..."); - readFromXml(parser); - //Log.i(TAG, "Finished filter: outerDepth=" + outerDepth + " depth=" - // + parser.getDepth() + " tag=" + parser.getName()); - } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <preferred-activities>: " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } - return true; - } - } - - static class GrantedPermissions { - int pkgFlags; - - HashSet<String> grantedPermissions = new HashSet<String>(); - int[] gids; - - GrantedPermissions(int pkgFlags) { - setFlags(pkgFlags); - } - - GrantedPermissions(GrantedPermissions base) { - pkgFlags = base.pkgFlags; - grantedPermissions = (HashSet<String>) base.grantedPermissions.clone(); - - if (base.gids != null) { - gids = base.gids.clone(); - } - } - - void setFlags(int pkgFlags) { - this.pkgFlags = pkgFlags & ( - ApplicationInfo.FLAG_SYSTEM | - ApplicationInfo.FLAG_FORWARD_LOCK | - ApplicationInfo.FLAG_EXTERNAL_STORAGE); - } - } - - /** - * Settings base class for pending and resolved classes. - */ - static class PackageSettingBase extends GrantedPermissions { - final String name; - final String realName; - File codePath; - String codePathString; - File resourcePath; - String resourcePathString; - String nativeLibraryPathString; - long timeStamp; - long firstInstallTime; - long lastUpdateTime; - int versionCode; - - boolean uidError; - - PackageSignatures signatures = new PackageSignatures(); - - boolean permissionsFixed; - boolean haveGids; - - // Whether this package is currently stopped, thus can not be - // started until explicitly launched by the user. - public boolean stopped; - - // Set to true if we have never launched this app. - public boolean notLaunched; - - /* Explicitly disabled components */ - HashSet<String> disabledComponents = new HashSet<String>(0); - /* Explicitly enabled components */ - HashSet<String> enabledComponents = new HashSet<String>(0); - int enabled = COMPONENT_ENABLED_STATE_DEFAULT; - int installStatus = PKG_INSTALL_COMPLETE; - - PackageSettingBase origPackage; - - /* package name of the app that installed this package */ - String installerPackageName; - - PackageSettingBase(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, int pVersionCode, int pkgFlags) { - super(pkgFlags); - this.name = name; - this.realName = realName; - init(codePath, resourcePath, nativeLibraryPathString, pVersionCode); - } - - /** - * New instance of PackageSetting with one-level-deep cloning. - */ - PackageSettingBase(PackageSettingBase base) { - super(base); - - name = base.name; - realName = base.realName; - codePath = base.codePath; - codePathString = base.codePathString; - resourcePath = base.resourcePath; - resourcePathString = base.resourcePathString; - nativeLibraryPathString = base.nativeLibraryPathString; - timeStamp = base.timeStamp; - firstInstallTime = base.firstInstallTime; - lastUpdateTime = base.lastUpdateTime; - versionCode = base.versionCode; - - uidError = base.uidError; - - signatures = new PackageSignatures(base.signatures); - - permissionsFixed = base.permissionsFixed; - haveGids = base.haveGids; - stopped = base.stopped; - notLaunched = base.notLaunched; - - disabledComponents = (HashSet<String>) base.disabledComponents.clone(); - - enabledComponents = (HashSet<String>) base.enabledComponents.clone(); - - enabled = base.enabled; - installStatus = base.installStatus; - - origPackage = base.origPackage; - - installerPackageName = base.installerPackageName; - } - - void init(File codePath, File resourcePath, String nativeLibraryPathString, - int pVersionCode) { - this.codePath = codePath; - this.codePathString = codePath.toString(); - this.resourcePath = resourcePath; - this.resourcePathString = resourcePath.toString(); - this.nativeLibraryPathString = nativeLibraryPathString; - this.versionCode = pVersionCode; - } - - public void setInstallerPackageName(String packageName) { - installerPackageName = packageName; - } - - String getInstallerPackageName() { - return installerPackageName; - } - - public void setInstallStatus(int newStatus) { - installStatus = newStatus; - } - - public int getInstallStatus() { - return installStatus; - } - - public void setTimeStamp(long newStamp) { - timeStamp = newStamp; - } - - /** - * Make a shallow copy of this package settings. - */ - public void copyFrom(PackageSettingBase base) { - grantedPermissions = base.grantedPermissions; - gids = base.gids; - - timeStamp = base.timeStamp; - firstInstallTime = base.firstInstallTime; - lastUpdateTime = base.lastUpdateTime; - signatures = base.signatures; - permissionsFixed = base.permissionsFixed; - haveGids = base.haveGids; - stopped = base.stopped; - notLaunched = base.notLaunched; - disabledComponents = base.disabledComponents; - enabledComponents = base.enabledComponents; - enabled = base.enabled; - installStatus = base.installStatus; - } - - boolean enableComponentLP(String componentClassName) { - boolean changed = disabledComponents.remove(componentClassName); - changed |= enabledComponents.add(componentClassName); - return changed; - } - - boolean disableComponentLP(String componentClassName) { - boolean changed = enabledComponents.remove(componentClassName); - changed |= disabledComponents.add(componentClassName); - return changed; - } - - boolean restoreComponentLP(String componentClassName) { - boolean changed = enabledComponents.remove(componentClassName); - changed |= disabledComponents.remove(componentClassName); - return changed; - } - - int currentEnabledStateLP(String componentName) { - if (enabledComponents.contains(componentName)) { - return COMPONENT_ENABLED_STATE_ENABLED; - } else if (disabledComponents.contains(componentName)) { - return COMPONENT_ENABLED_STATE_DISABLED; - } else { - return COMPONENT_ENABLED_STATE_DEFAULT; - } - } - } - - /** - * Settings data for a particular package we know about. - */ - static final class PackageSetting extends PackageSettingBase { - int userId; - PackageParser.Package pkg; - SharedUserSetting sharedUser; - - PackageSetting(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, int pVersionCode, int pkgFlags) { - super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode, - pkgFlags); - } - - /** - * New instance of PackageSetting replicating the original settings. - * Note that it keeps the same PackageParser.Package instance. - */ - PackageSetting(PackageSetting orig) { - super(orig); - - userId = orig.userId; - pkg = orig.pkg; - sharedUser = orig.sharedUser; - } - - @Override - public String toString() { - return "PackageSetting{" - + Integer.toHexString(System.identityHashCode(this)) - + " " + name + "/" + userId + "}"; - } - } - - /** - * Settings data for a particular shared user ID we know about. - */ - static final class SharedUserSetting extends GrantedPermissions { - final String name; - int userId; - final HashSet<PackageSetting> packages = new HashSet<PackageSetting>(); - final PackageSignatures signatures = new PackageSignatures(); - - SharedUserSetting(String _name, int _pkgFlags) { - super(_pkgFlags); - name = _name; - } - - @Override - public String toString() { - return "SharedUserSetting{" - + Integer.toHexString(System.identityHashCode(this)) - + " " + name + "/" + userId + "}"; - } - } - - /** - * Holds information about dynamic settings. - */ - private static final class Settings { - private final File mSettingsFilename; - private final File mBackupSettingsFilename; - private final File mPackageListFilename; - private final File mStoppedPackagesFilename; - private final File mBackupStoppedPackagesFilename; - private final HashMap<String, PackageSetting> mPackages = - new HashMap<String, PackageSetting>(); - // List of replaced system applications - final HashMap<String, PackageSetting> mDisabledSysPackages = - new HashMap<String, PackageSetting>(); - - // These are the last platform API version we were using for - // the apps installed on internal and external storage. It is - // used to grant newer permissions one time during a system upgrade. - int mInternalSdkPlatform; - int mExternalSdkPlatform; - - // The user's preferred activities associated with particular intent - // filters. - private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities = - new IntentResolver<PreferredActivity, PreferredActivity>() { - @Override - protected String packageForFilter(PreferredActivity filter) { - return filter.mPref.mComponent.getPackageName(); - } - @Override - protected void dumpFilter(PrintWriter out, String prefix, - PreferredActivity filter) { - filter.mPref.dump(out, prefix, filter); - } - }; - private final HashMap<String, SharedUserSetting> mSharedUsers = - new HashMap<String, SharedUserSetting>(); - private final ArrayList<Object> mUserIds = new ArrayList<Object>(); - private final SparseArray<Object> mOtherUserIds = - new SparseArray<Object>(); - - // For reading/writing settings file. - private final ArrayList<Signature> mPastSignatures = - new ArrayList<Signature>(); - - // Mapping from permission names to info about them. - final HashMap<String, BasePermission> mPermissions = - new HashMap<String, BasePermission>(); - - // Mapping from permission tree names to info about them. - final HashMap<String, BasePermission> mPermissionTrees = - new HashMap<String, BasePermission>(); - - // Packages that have been uninstalled and still need their external - // storage data deleted. - final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>(); - - // Packages that have been renamed since they were first installed. - // Keys are the new names of the packages, values are the original - // names. The packages appear everwhere else under their original - // names. - final HashMap<String, String> mRenamedPackages = new HashMap<String, String>(); - - private final StringBuilder mReadMessages = new StringBuilder(); - - private static final class PendingPackage extends PackageSettingBase { - final int sharedId; - - PendingPackage(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) { - super(name, realName, codePath, resourcePath, nativeLibraryPathString, - pVersionCode, pkgFlags); - this.sharedId = sharedId; - } - } - private final ArrayList<PendingPackage> mPendingPackages - = new ArrayList<PendingPackage>(); - - Settings() { - File dataDir = Environment.getDataDirectory(); - File systemDir = new File(dataDir, "system"); - // TODO(oam): This secure dir creation needs to be moved somewhere else (later) - File systemSecureDir = new File(dataDir, "secure/system"); - systemDir.mkdirs(); - systemSecureDir.mkdirs(); - FileUtils.setPermissions(systemDir.toString(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG - |FileUtils.S_IROTH|FileUtils.S_IXOTH, - -1, -1); - FileUtils.setPermissions(systemSecureDir.toString(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG - |FileUtils.S_IROTH|FileUtils.S_IXOTH, - -1, -1); - mSettingsFilename = new File(systemDir, "packages.xml"); - mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); - mPackageListFilename = new File(systemDir, "packages.list"); - mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml"); - mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml"); - } - - PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage, - String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) { - final String name = pkg.packageName; - PackageSetting p = getPackageLP(name, origPackage, realName, sharedUser, codePath, - resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add); - return p; - } - - PackageSetting peekPackageLP(String name) { - return mPackages.get(name); - /* - PackageSetting p = mPackages.get(name); - if (p != null && p.codePath.getPath().equals(codePath)) { - return p; - } - return null; - */ - } - - void setInstallStatus(String pkgName, int status) { - PackageSetting p = mPackages.get(pkgName); - if(p != null) { - if(p.getInstallStatus() != status) { - p.setInstallStatus(status); - } - } - } + // ------- apps on sdcard specific code ------- + static final boolean DEBUG_SD_INSTALL = false; - void setInstallerPackageName(String pkgName, - String installerPkgName) { - PackageSetting p = mPackages.get(pkgName); - if(p != null) { - p.setInstallerPackageName(installerPkgName); - } - } + private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD"; - String getInstallerPackageName(String pkgName) { - PackageSetting p = mPackages.get(pkgName); - return (p == null) ? null : p.getInstallerPackageName(); - } + private static final String SD_ENCRYPTION_ALGORITHM = "AES"; - int getInstallStatus(String pkgName) { - PackageSetting p = mPackages.get(pkgName); - if(p != null) { - return p.getInstallStatus(); - } - return -1; - } + private boolean mMediaMounted = false; - SharedUserSetting getSharedUserLP(String name, - int pkgFlags, boolean create) { - SharedUserSetting s = mSharedUsers.get(name); - if (s == null) { - if (!create) { + private String getEncryptKey() { + try { + String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString( + SD_ENCRYPTION_KEYSTORE_NAME); + if (sdEncKey == null) { + sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128, + SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME); + if (sdEncKey == null) { + Slog.e(TAG, "Failed to create encryption keys"); return null; } - s = new SharedUserSetting(name, pkgFlags); - if (MULTIPLE_APPLICATION_UIDS) { - s.userId = newUserIdLP(s); - } else { - s.userId = FIRST_APPLICATION_UID; - } - Log.i(TAG, "New shared user " + name + ": id=" + s.userId); - // < 0 means we couldn't assign a userid; fall out and return - // s, which is currently null - if (s.userId >= 0) { - mSharedUsers.put(name, s); - } - } - - return s; - } - - boolean disableSystemPackageLP(String name) { - PackageSetting p = mPackages.get(name); - if(p == null) { - Log.w(TAG, "Package:"+name+" is not an installed package"); - return false; - } - PackageSetting dp = mDisabledSysPackages.get(name); - // always make sure the system package code and resource paths dont change - if (dp == null) { - if((p.pkg != null) && (p.pkg.applicationInfo != null)) { - p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - } - mDisabledSysPackages.put(name, p); - - // a little trick... when we install the new package, we don't - // want to modify the existing PackageSetting for the built-in - // version. so at this point we need a new PackageSetting that - // is okay to muck with. - PackageSetting newp = new PackageSetting(p); - replacePackageLP(name, newp); - return true; - } - return false; - } - - PackageSetting enableSystemPackageLP(String name) { - PackageSetting p = mDisabledSysPackages.get(name); - if(p == null) { - Log.w(TAG, "Package:"+name+" is not disabled"); - return null; - } - // Reset flag in ApplicationInfo object - if((p.pkg != null) && (p.pkg.applicationInfo != null)) { - p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - } - PackageSetting ret = addPackageLP(name, p.realName, p.codePath, p.resourcePath, - p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags); - mDisabledSysPackages.remove(name); - return ret; - } - - PackageSetting addPackageLP(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, int uid, int vc, int pkgFlags) { - PackageSetting p = mPackages.get(name); - if (p != null) { - if (p.userId == uid) { - return p; - } - reportSettingsProblem(Log.ERROR, - "Adding duplicate package, keeping first: " + name); - return null; - } - p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, - vc, pkgFlags); - p.userId = uid; - if (addUserIdLP(uid, p, name)) { - mPackages.put(name, p); - return p; } + return sdEncKey; + } catch (NoSuchAlgorithmException nsae) { + Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae); return null; - } - - SharedUserSetting addSharedUserLP(String name, int uid, int pkgFlags) { - SharedUserSetting s = mSharedUsers.get(name); - if (s != null) { - if (s.userId == uid) { - return s; - } - reportSettingsProblem(Log.ERROR, - "Adding duplicate shared user, keeping first: " + name); - return null; - } - s = new SharedUserSetting(name, pkgFlags); - s.userId = uid; - if (addUserIdLP(uid, s, name)) { - mSharedUsers.put(name, s); - return s; - } + } catch (IOException ioe) { + Slog.e(TAG, "Failed to retrieve encryption keys with exception: " + ioe); return null; } - // Transfer ownership of permissions from one package to another. - private void transferPermissions(String origPkg, String newPkg) { - // Transfer ownership of permissions to the new package. - for (int i=0; i<2; i++) { - HashMap<String, BasePermission> permissions = - i == 0 ? mPermissionTrees : mPermissions; - for (BasePermission bp : permissions.values()) { - if (origPkg.equals(bp.sourcePackage)) { - if (DEBUG_UPGRADE) Log.v(TAG, - "Moving permission " + bp.name - + " from pkg " + bp.sourcePackage - + " to " + newPkg); - bp.sourcePackage = newPkg; - bp.packageSetting = null; - bp.perm = null; - if (bp.pendingInfo != null) { - bp.pendingInfo.packageName = newPkg; - } - bp.uid = 0; - bp.gids = null; - } - } - } - } - - private PackageSetting getPackageLP(String name, PackageSetting origPackage, - String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) { - PackageSetting p = mPackages.get(name); - if (p != null) { - if (!p.codePath.equals(codePath)) { - // Check to see if its a disabled system app - if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { - // This is an updated system app with versions in both system - // and data partition. Just let the most recent version - // take precedence. - Slog.w(TAG, "Trying to update system app code path from " + - p.codePathString + " to " + codePath.toString()); - } else { - // Just a change in the code path is not an issue, but - // let's log a message about it. - Slog.i(TAG, "Package " + name + " codePath changed from " + p.codePath - + " to " + codePath + "; Retaining data and using new"); - /* - * Since we've changed paths, we need to prefer the new - * native library path over the one stored in the - * package settings since we might have moved from - * internal to external storage or vice versa. - */ - p.nativeLibraryPathString = nativeLibraryPathString; - } - } - if (p.sharedUser != sharedUser) { - reportSettingsProblem(Log.WARN, - "Package " + name + " shared user changed from " - + (p.sharedUser != null ? p.sharedUser.name : "<nothing>") - + " to " - + (sharedUser != null ? sharedUser.name : "<nothing>") - + "; replacing with new"); - p = null; - } else { - if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) { - // If what we are scanning is a system package, then - // make it so, regardless of whether it was previously - // installed only in the data partition. - p.pkgFlags |= ApplicationInfo.FLAG_SYSTEM; - } - } - } - if (p == null) { - // Create a new PackageSettings entry. this can end up here because - // of code path mismatch or user id mismatch of an updated system partition - if (!create) { - return null; - } - if (origPackage != null) { - // We are consuming the data from an existing package. - p = new PackageSetting(origPackage.name, name, codePath, resourcePath, - nativeLibraryPathString, vc, pkgFlags); - if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name - + " is adopting original package " + origPackage.name); - // Note that we will retain the new package's signature so - // that we can keep its data. - PackageSignatures s = p.signatures; - p.copyFrom(origPackage); - p.signatures = s; - p.sharedUser = origPackage.sharedUser; - p.userId = origPackage.userId; - p.origPackage = origPackage; - mRenamedPackages.put(name, origPackage.name); - name = origPackage.name; - // Update new package state. - p.setTimeStamp(codePath.lastModified()); - } else { - p = new PackageSetting(name, realName, codePath, resourcePath, - nativeLibraryPathString, vc, pkgFlags); - p.setTimeStamp(codePath.lastModified()); - p.sharedUser = sharedUser; - // If this is not a system app, it starts out stopped. - if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { - if (DEBUG_STOPPED) { - RuntimeException e = new RuntimeException("here"); - e.fillInStackTrace(); - Slog.i(TAG, "Stopping package " + name, e); - } - p.stopped = true; - p.notLaunched = true; - } - if (sharedUser != null) { - p.userId = sharedUser.userId; - } else if (MULTIPLE_APPLICATION_UIDS) { - // Clone the setting here for disabled system packages - PackageSetting dis = mDisabledSysPackages.get(name); - if (dis != null) { - // For disabled packages a new setting is created - // from the existing user id. This still has to be - // added to list of user id's - // Copy signatures from previous setting - if (dis.signatures.mSignatures != null) { - p.signatures.mSignatures = dis.signatures.mSignatures.clone(); - } - p.userId = dis.userId; - // Clone permissions - p.grantedPermissions = new HashSet<String>(dis.grantedPermissions); - // Clone component info - p.disabledComponents = new HashSet<String>(dis.disabledComponents); - p.enabledComponents = new HashSet<String>(dis.enabledComponents); - // Add new setting to list of user ids - addUserIdLP(p.userId, p, name); - } else { - // Assign new user id - p.userId = newUserIdLP(p); - } - } else { - p.userId = FIRST_APPLICATION_UID; - } - } - if (p.userId < 0) { - reportSettingsProblem(Log.WARN, - "Package " + name + " could not be assigned a valid uid"); - return null; - } - if (add) { - // Finish adding new package by adding it and updating shared - // user preferences - addPackageSettingLP(p, name, sharedUser); - } - } - return p; - } - - private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) { - p.pkg = pkg; - pkg.mSetEnabled = p.enabled; - pkg.mSetStopped = p.stopped; - final String codePath = pkg.applicationInfo.sourceDir; - final String resourcePath = pkg.applicationInfo.publicSourceDir; - // Update code path if needed - if (!codePath.equalsIgnoreCase(p.codePathString)) { - Slog.w(TAG, "Code path for pkg : " + p.pkg.packageName + - " changing from " + p.codePathString + " to " + codePath); - p.codePath = new File(codePath); - p.codePathString = codePath; - } - //Update resource path if needed - if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) { - Slog.w(TAG, "Resource path for pkg : " + p.pkg.packageName + - " changing from " + p.resourcePathString + " to " + resourcePath); - p.resourcePath = new File(resourcePath); - p.resourcePathString = resourcePath; - } - // Update the native library path if needed - final String nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir; - if (nativeLibraryPath != null - && !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) { - p.nativeLibraryPathString = nativeLibraryPath; - } - // Update version code if needed - if (pkg.mVersionCode != p.versionCode) { - p.versionCode = pkg.mVersionCode; - } - // Update signatures if needed. - if (p.signatures.mSignatures == null) { - p.signatures.assignSignatures(pkg.mSignatures); - } - // If this app defines a shared user id initialize - // the shared user signatures as well. - if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) { - p.sharedUser.signatures.assignSignatures(pkg.mSignatures); - } - addPackageSettingLP(p, pkg.packageName, p.sharedUser); - } - - // Utility method that adds a PackageSetting to mPackages and - // completes updating the shared user attributes - private void addPackageSettingLP(PackageSetting p, String name, - SharedUserSetting sharedUser) { - mPackages.put(name, p); - if (sharedUser != null) { - if (p.sharedUser != null && p.sharedUser != sharedUser) { - reportSettingsProblem(Log.ERROR, - "Package " + p.name + " was user " - + p.sharedUser + " but is now " + sharedUser - + "; I am not changing its files so it will probably fail!"); - p.sharedUser.packages.remove(p); - } else if (p.userId != sharedUser.userId) { - reportSettingsProblem(Log.ERROR, - "Package " + p.name + " was user id " + p.userId - + " but is now user " + sharedUser - + " with id " + sharedUser.userId - + "; I am not changing its files so it will probably fail!"); - } - - sharedUser.packages.add(p); - p.sharedUser = sharedUser; - p.userId = sharedUser.userId; - } - } + } - /* - * Update the shared user setting when a package using - * specifying the shared user id is removed. The gids - * associated with each permission of the deleted package - * are removed from the shared user's gid list only if its - * not in use by other permissions of packages in the - * shared user setting. - */ - private void updateSharedUserPermsLP(PackageSetting deletedPs, int[] globalGids) { - if ( (deletedPs == null) || (deletedPs.pkg == null)) { - Slog.i(TAG, "Trying to update info for null package. Just ignoring"); - return; - } - // No sharedUserId - if (deletedPs.sharedUser == null) { - return; - } - SharedUserSetting sus = deletedPs.sharedUser; - // Update permissions - for (String eachPerm: deletedPs.pkg.requestedPermissions) { - boolean used = false; - if (!sus.grantedPermissions.contains (eachPerm)) { + /* package */static String getTempContainerId() { + int tmpIdx = 1; + String list[] = PackageHelper.getSecureContainerList(); + if (list != null) { + for (final String name : list) { + // Ignore null and non-temporary container entries + if (name == null || !name.startsWith(mTempContainerPrefix)) { continue; } - for (PackageSetting pkg:sus.packages) { - if (pkg.pkg != null && - !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) && - pkg.pkg.requestedPermissions.contains(eachPerm)) { - used = true; - break; - } - } - if (!used) { - // can safely delete this permission from list - sus.grantedPermissions.remove(eachPerm); - } - } - // Update gids - int newGids[] = globalGids; - for (String eachPerm : sus.grantedPermissions) { - BasePermission bp = mPermissions.get(eachPerm); - if (bp != null) { - newGids = appendInts(newGids, bp.gids); - } - } - sus.gids = newGids; - } - private int removePackageLP(String name) { - PackageSetting p = mPackages.get(name); - if (p != null) { - mPackages.remove(name); - if (p.sharedUser != null) { - p.sharedUser.packages.remove(p); - if (p.sharedUser.packages.size() == 0) { - mSharedUsers.remove(p.sharedUser.name); - removeUserIdLP(p.sharedUser.userId); - return p.sharedUser.userId; + String subStr = name.substring(mTempContainerPrefix.length()); + try { + int cid = Integer.parseInt(subStr); + if (cid >= tmpIdx) { + tmpIdx = cid + 1; } - } else { - removeUserIdLP(p.userId); - return p.userId; - } - } - return -1; - } - - private void replacePackageLP(String name, PackageSetting newp) { - PackageSetting p = mPackages.get(name); - if (p != null) { - if (p.sharedUser != null) { - p.sharedUser.packages.remove(p); - p.sharedUser.packages.add(newp); - } else { - replaceUserIdLP(p.userId, newp); - } - } - mPackages.put(name, newp); - } - - private boolean addUserIdLP(int uid, Object obj, Object name) { - if (uid >= FIRST_APPLICATION_UID + MAX_APPLICATION_UIDS) { - return false; - } - - if (uid >= FIRST_APPLICATION_UID) { - int N = mUserIds.size(); - final int index = uid - FIRST_APPLICATION_UID; - while (index >= N) { - mUserIds.add(null); - N++; - } - if (mUserIds.get(index) != null) { - reportSettingsProblem(Log.ERROR, - "Adding duplicate user id: " + uid - + " name=" + name); - return false; - } - mUserIds.set(index, obj); - } else { - if (mOtherUserIds.get(uid) != null) { - reportSettingsProblem(Log.ERROR, - "Adding duplicate shared id: " + uid - + " name=" + name); - return false; - } - mOtherUserIds.put(uid, obj); - } - return true; - } - - public Object getUserIdLP(int uid) { - if (uid >= FIRST_APPLICATION_UID) { - int N = mUserIds.size(); - final int index = uid - FIRST_APPLICATION_UID; - return index < N ? mUserIds.get(index) : null; - } else { - return mOtherUserIds.get(uid); - } - } - - private Set<String> findPackagesWithFlag(int flag) { - Set<String> ret = new HashSet<String>(); - for (PackageSetting ps : mPackages.values()) { - // Has to match atleast all the flag bits set on flag - if ((ps.pkgFlags & flag) == flag) { - ret.add(ps.name); + } catch (NumberFormatException e) { } } - return ret; } + return mTempContainerPrefix + tmpIdx; + } - private void removeUserIdLP(int uid) { - if (uid >= FIRST_APPLICATION_UID) { - int N = mUserIds.size(); - final int index = uid - FIRST_APPLICATION_UID; - if (index < N) mUserIds.set(index, null); - } else { - mOtherUserIds.remove(uid); - } + /* + * Update media status on PackageManager. + */ + public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) { + int callingUid = Binder.getCallingUid(); + if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { + throw new SecurityException("Media status can only be updated by the system"); } - - private void replaceUserIdLP(int uid, Object obj) { - if (uid >= FIRST_APPLICATION_UID) { - int N = mUserIds.size(); - final int index = uid - FIRST_APPLICATION_UID; - if (index < N) mUserIds.set(index, obj); - } else { - mOtherUserIds.put(uid, obj); - } - } - - void writeStoppedLP() { - // Keep the old stopped packages around until we know the new ones have - // been successfully written. - if (mStoppedPackagesFilename.exists()) { - // Presence of backup settings file indicates that we failed - // to persist packages earlier. So preserve the older - // backup for future reference since the current packages - // might have been corrupted. - if (!mBackupStoppedPackagesFilename.exists()) { - if (!mStoppedPackagesFilename.renameTo(mBackupStoppedPackagesFilename)) { - Log.wtf(TAG, "Unable to backup package manager stopped packages, " - + "current changes will be lost at reboot"); - return; - } - } else { - mStoppedPackagesFilename.delete(); - Slog.w(TAG, "Preserving older stopped packages backup"); - } - } - - try { - FileOutputStream fstr = new FileOutputStream(mStoppedPackagesFilename); - BufferedOutputStream str = new BufferedOutputStream(fstr); - - //XmlSerializer serializer = XmlUtils.serializerInstance(); - XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(str, "utf-8"); - serializer.startDocument(null, true); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - serializer.startTag(null, "stopped-packages"); - - for (PackageSetting pkg : mPackages.values()) { - if (pkg.stopped) { - serializer.startTag(null, "pkg"); - serializer.attribute(null, "name", pkg.name); - if (pkg.notLaunched) { - serializer.attribute(null, "nl", "1"); - } - serializer.endTag(null, "pkg"); - } - } - - serializer.endTag(null, "stopped-packages"); - - serializer.endDocument(); - - str.flush(); - FileUtils.sync(fstr); - str.close(); - - // New settings successfully written, old ones are no longer - // needed. - mBackupStoppedPackagesFilename.delete(); - FileUtils.setPermissions(mStoppedPackagesFilename.toString(), - FileUtils.S_IRUSR|FileUtils.S_IWUSR - |FileUtils.S_IRGRP|FileUtils.S_IWGRP - |FileUtils.S_IROTH, - -1, -1); - - // Done, all is good! + // reader; this apparently protects mMediaMounted, but should probably + // be a different lock in that case. + synchronized (mPackages) { + Log.i(TAG, "Updating external media status from " + + (mMediaMounted ? "mounted" : "unmounted") + " to " + + (mediaStatus ? "mounted" : "unmounted")); + if (DEBUG_SD_INSTALL) + Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus + + ", mMediaMounted=" + mMediaMounted); + if (mediaStatus == mMediaMounted) { + final Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 + : 0, -1); + mHandler.sendMessage(msg); return; - - } catch(java.io.IOException e) { - Log.wtf(TAG, "Unable to write package manager stopped packages, " - + " current changes will be lost at reboot", e); - } - - // Clean up partially written files - if (mStoppedPackagesFilename.exists()) { - if (!mStoppedPackagesFilename.delete()) { - Log.i(TAG, "Failed to clean up mangled file: " + mStoppedPackagesFilename); - } } + mMediaMounted = mediaStatus; } - - // Note: assumed "stopped" field is already cleared in all packages. - void readStoppedLP() { - FileInputStream str = null; - if (mBackupStoppedPackagesFilename.exists()) { - try { - str = new FileInputStream(mBackupStoppedPackagesFilename); - mReadMessages.append("Reading from backup stopped packages file\n"); - reportSettingsProblem(Log.INFO, "Need to read from backup stopped packages file"); - if (mSettingsFilename.exists()) { - // If both the backup and normal file exist, we - // ignore the normal one since it might have been - // corrupted. - Slog.w(TAG, "Cleaning up stopped packages file " - + mStoppedPackagesFilename); - mStoppedPackagesFilename.delete(); - } - } catch (java.io.IOException e) { - // We'll try for the normal settings file. - } + // Queue up an async operation since the package installation may take a + // little while. + mHandler.post(new Runnable() { + public void run() { + // TODO fix this; this does nothing. + mHandler.removeCallbacks(this); + updateExternalMediaStatusInner(mediaStatus, reportStatus); } + }); + } - try { - if (str == null) { - if (!mStoppedPackagesFilename.exists()) { - mReadMessages.append("No stopped packages file found\n"); - reportSettingsProblem(Log.INFO, "No stopped packages file file; " - + "assuming all started"); - // At first boot, make sure no packages are stopped. - // We usually want to have third party apps initialize - // in the stopped state, but not at first boot. - for (PackageSetting pkg : mPackages.values()) { - pkg.stopped = false; - pkg.notLaunched = false; - } - return; - } - str = new FileInputStream(mStoppedPackagesFilename); - } - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(str, null); - - int type; - while ((type=parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - ; - } - - if (type != XmlPullParser.START_TAG) { - mReadMessages.append("No start tag found in stopped packages file\n"); - reportSettingsProblem(Log.WARN, - "No start tag found in package manager stopped packages"); - return; - } - - int outerDepth = parser.getDepth(); - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { + /* + * Collect information of applications on external media, map them against + * existing containers and update information based on current mount status. + * Please note that we always have to report status if reportStatus has been + * set to true especially when unloading packages. + */ + private void updateExternalMediaStatusInner(boolean mediaStatus, boolean reportStatus) { + // Collection of uids + int uidArr[] = null; + // Collection of stale containers + HashSet<String> removeCids = new HashSet<String>(); + // Collection of packages on external media with valid containers. + HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>(); + // Get list of secure containers. + final String list[] = PackageHelper.getSecureContainerList(); + if (list == null || list.length == 0) { + Log.i(TAG, "No secure containers on sdcard"); + } else { + // Process list of secure containers and categorize them + // as active or stale based on their package internal state. + int uidList[] = new int[list.length]; + int num = 0; + // reader + synchronized (mPackages) { + for (String cid : list) { + SdInstallArgs args = new SdInstallArgs(cid); + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Processing container " + cid); + String pkgName = args.getPackageName(); + if (pkgName == null) { + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Container : " + cid + " stale"); + removeCids.add(cid); continue; } - - String tagName = parser.getName(); - if (tagName.equals("pkg")) { - String name = parser.getAttributeValue(null, "name"); - PackageSetting ps = mPackages.get(name); - if (ps != null) { - ps.stopped = true; - if ("1".equals(parser.getAttributeValue(null, "nl"))) { - ps.notLaunched = true; - } - } else { - Slog.w(TAG, "No package known for stopped package: " + name); + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Looking for pkg : " + pkgName); + PackageSetting ps = mSettings.mPackages.get(pkgName); + // The package status is changed only if the code path + // matches between settings and the container id. + if (ps != null && ps.codePathString != null + && ps.codePathString.equals(args.getCodePath())) { + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName + + " at code path: " + ps.codePathString); + // We do have a valid package installed on sdcard + processCids.put(args, ps.codePathString); + int uid = ps.userId; + if (uid != -1) { + uidList[num++] = uid; } - XmlUtils.skipCurrentTag(parser); } else { - Slog.w(TAG, "Unknown element under <stopped-packages>: " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); + // Stale container on sdcard. Just delete + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Container : " + cid + " stale"); + removeCids.add(cid); } } - - str.close(); - - } catch(XmlPullParserException e) { - mReadMessages.append("Error reading: " + e.toString()); - reportSettingsProblem(Log.ERROR, "Error reading stopped packages: " + e); - Log.wtf(TAG, "Error reading package manager stopped packages", e); - - } catch(java.io.IOException e) { - mReadMessages.append("Error reading: " + e.toString()); - reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); - Log.wtf(TAG, "Error reading package manager stopped packages", e); - } - } - - void writeLP() { - //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); - // Keep the old settings around until we know the new ones have - // been successfully written. - if (mSettingsFilename.exists()) { - // Presence of backup settings file indicates that we failed - // to persist settings earlier. So preserve the older - // backup for future reference since the current settings - // might have been corrupted. - if (!mBackupSettingsFilename.exists()) { - if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) { - Log.wtf(TAG, "Unable to backup package manager settings, " - + " current changes will be lost at reboot"); - return; + if (num > 0) { + // Sort uid list + Arrays.sort(uidList, 0, num); + // Throw away duplicates + uidArr = new int[num]; + uidArr[0] = uidList[0]; + int di = 0; + for (int i = 1; i < num; i++) { + if (uidList[i - 1] != uidList[i]) { + uidArr[di++] = uidList[i]; } - } else { - mSettingsFilename.delete(); - Slog.w(TAG, "Preserving older settings backup"); } } - - mPastSignatures.clear(); - - try { - FileOutputStream fstr = new FileOutputStream(mSettingsFilename); - BufferedOutputStream str = new BufferedOutputStream(fstr); - - //XmlSerializer serializer = XmlUtils.serializerInstance(); - XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(str, "utf-8"); - serializer.startDocument(null, true); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - serializer.startTag(null, "packages"); - - serializer.startTag(null, "last-platform-version"); - serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform)); - serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform)); - serializer.endTag(null, "last-platform-version"); - - serializer.startTag(null, "permission-trees"); - for (BasePermission bp : mPermissionTrees.values()) { - writePermission(serializer, bp); - } - serializer.endTag(null, "permission-trees"); - - serializer.startTag(null, "permissions"); - for (BasePermission bp : mPermissions.values()) { - writePermission(serializer, bp); - } - serializer.endTag(null, "permissions"); - - for (PackageSetting pkg : mPackages.values()) { - writePackage(serializer, pkg); - } - - for (PackageSetting pkg : mDisabledSysPackages.values()) { - writeDisabledSysPackage(serializer, pkg); - } - - serializer.startTag(null, "preferred-activities"); - for (PreferredActivity pa : mPreferredActivities.filterSet()) { - serializer.startTag(null, "item"); - pa.writeToXml(serializer); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "preferred-activities"); - - for (SharedUserSetting usr : mSharedUsers.values()) { - serializer.startTag(null, "shared-user"); - serializer.attribute(null, "name", usr.name); - serializer.attribute(null, "userId", - Integer.toString(usr.userId)); - usr.signatures.writeXml(serializer, "sigs", mPastSignatures); - serializer.startTag(null, "perms"); - for (String name : usr.grantedPermissions) { - serializer.startTag(null, "item"); - serializer.attribute(null, "name", name); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "perms"); - serializer.endTag(null, "shared-user"); - } - - if (mPackagesToBeCleaned.size() > 0) { - for (int i=0; i<mPackagesToBeCleaned.size(); i++) { - serializer.startTag(null, "cleaning-package"); - serializer.attribute(null, "name", mPackagesToBeCleaned.get(i)); - serializer.endTag(null, "cleaning-package"); - } - } - - if (mRenamedPackages.size() > 0) { - for (HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) { - serializer.startTag(null, "renamed-package"); - serializer.attribute(null, "new", e.getKey()); - serializer.attribute(null, "old", e.getValue()); - serializer.endTag(null, "renamed-package"); - } - } - - serializer.endTag(null, "packages"); - - serializer.endDocument(); - - str.flush(); - FileUtils.sync(fstr); - str.close(); - - // New settings successfully written, old ones are no longer - // needed. - mBackupSettingsFilename.delete(); - FileUtils.setPermissions(mSettingsFilename.toString(), - FileUtils.S_IRUSR|FileUtils.S_IWUSR - |FileUtils.S_IRGRP|FileUtils.S_IWGRP - |FileUtils.S_IROTH, - -1, -1); - - // Write package list file now, use a JournaledFile. - // - File tempFile = new File(mPackageListFilename.toString() + ".tmp"); - JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile); - - fstr = new FileOutputStream(journal.chooseForWrite()); - str = new BufferedOutputStream(fstr); - try { - StringBuilder sb = new StringBuilder(); - for (PackageSetting pkg : mPackages.values()) { - ApplicationInfo ai = pkg.pkg.applicationInfo; - String dataPath = ai.dataDir; - boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - - // Avoid any application that has a space in its path - // or that is handled by the system. - if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID) - continue; - - // we store on each line the following information for now: - // - // pkgName - package name - // userId - application-specific user id - // debugFlag - 0 or 1 if the package is debuggable. - // dataPath - path to package's data path - // - // NOTE: We prefer not to expose all ApplicationInfo flags for now. - // - // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS - // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES: - // system/core/run-as/run-as.c - // - sb.setLength(0); - sb.append(ai.packageName); - sb.append(" "); - sb.append((int)ai.uid); - sb.append(isDebug ? " 1 " : " 0 "); - sb.append(dataPath); - sb.append("\n"); - str.write(sb.toString().getBytes()); - } - str.flush(); - FileUtils.sync(fstr); - str.close(); - journal.commit(); - } - catch (Exception e) { - journal.rollback(); - } - - FileUtils.setPermissions(mPackageListFilename.toString(), - FileUtils.S_IRUSR|FileUtils.S_IWUSR - |FileUtils.S_IRGRP|FileUtils.S_IWGRP - |FileUtils.S_IROTH, - -1, -1); - - writeStoppedLP(); - - return; - - } catch(XmlPullParserException e) { - Log.wtf(TAG, "Unable to write package manager settings, " - + "current changes will be lost at reboot", e); - } catch(java.io.IOException e) { - Log.wtf(TAG, "Unable to write package manager settings, " - + "current changes will be lost at reboot", e); - } - // Clean up partially written files - if (mSettingsFilename.exists()) { - if (!mSettingsFilename.delete()) { - Log.wtf(TAG, "Failed to clean up mangled file: " + mSettingsFilename); - } - } - //Debug.stopMethodTracing(); - } - - void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg) - throws java.io.IOException { - serializer.startTag(null, "updated-package"); - serializer.attribute(null, "name", pkg.name); - if (pkg.realName != null) { - serializer.attribute(null, "realName", pkg.realName); - } - serializer.attribute(null, "codePath", pkg.codePathString); - serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); - serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); - serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime)); - serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } - if (pkg.nativeLibraryPathString != null) { - serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); - } - if (pkg.sharedUser == null) { - serializer.attribute(null, "userId", - Integer.toString(pkg.userId)); - } else { - serializer.attribute(null, "sharedUserId", - Integer.toString(pkg.userId)); - } - serializer.startTag(null, "perms"); - if (pkg.sharedUser == null) { - // If this is a shared user, the permissions will - // be written there. We still need to write an - // empty permissions list so permissionsFixed will - // be set. - for (final String name : pkg.grantedPermissions) { - BasePermission bp = mPermissions.get(name); - if (bp != null) { - // We only need to write signature or system permissions but this wont - // match the semantics of grantedPermissions. So write all permissions. - serializer.startTag(null, "item"); - serializer.attribute(null, "name", name); - serializer.endTag(null, "item"); - } - } - } - serializer.endTag(null, "perms"); - serializer.endTag(null, "updated-package"); - } - - void writePackage(XmlSerializer serializer, final PackageSetting pkg) - throws java.io.IOException { - serializer.startTag(null, "package"); - serializer.attribute(null, "name", pkg.name); - if (pkg.realName != null) { - serializer.attribute(null, "realName", pkg.realName); - } - serializer.attribute(null, "codePath", pkg.codePathString); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } - if (pkg.nativeLibraryPathString != null) { - serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); - } - serializer.attribute(null, "flags", - Integer.toString(pkg.pkgFlags)); - serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); - serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); - serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime)); - serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); - if (pkg.sharedUser == null) { - serializer.attribute(null, "userId", - Integer.toString(pkg.userId)); - } else { - serializer.attribute(null, "sharedUserId", - Integer.toString(pkg.userId)); - } - if (pkg.uidError) { - serializer.attribute(null, "uidError", "true"); - } - if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { - serializer.attribute(null, "enabled", - pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED - ? "true" : "false"); - } - if(pkg.installStatus == PKG_INSTALL_INCOMPLETE) { - serializer.attribute(null, "installStatus", "false"); - } - if (pkg.installerPackageName != null) { - serializer.attribute(null, "installer", pkg.installerPackageName); - } - pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); - if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { - serializer.startTag(null, "perms"); - if (pkg.sharedUser == null) { - // If this is a shared user, the permissions will - // be written there. We still need to write an - // empty permissions list so permissionsFixed will - // be set. - for (final String name : pkg.grantedPermissions) { - serializer.startTag(null, "item"); - serializer.attribute(null, "name", name); - serializer.endTag(null, "item"); - } - } - serializer.endTag(null, "perms"); - } - if (pkg.disabledComponents.size() > 0) { - serializer.startTag(null, "disabled-components"); - for (final String name : pkg.disabledComponents) { - serializer.startTag(null, "item"); - serializer.attribute(null, "name", name); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "disabled-components"); - } - if (pkg.enabledComponents.size() > 0) { - serializer.startTag(null, "enabled-components"); - for (final String name : pkg.enabledComponents) { - serializer.startTag(null, "item"); - serializer.attribute(null, "name", name); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "enabled-components"); - } - - serializer.endTag(null, "package"); - } - - void writePermission(XmlSerializer serializer, BasePermission bp) - throws XmlPullParserException, java.io.IOException { - if (bp.type != BasePermission.TYPE_BUILTIN - && bp.sourcePackage != null) { - serializer.startTag(null, "item"); - serializer.attribute(null, "name", bp.name); - serializer.attribute(null, "package", bp.sourcePackage); - if (bp.protectionLevel != - PermissionInfo.PROTECTION_NORMAL) { - serializer.attribute(null, "protection", - Integer.toString(bp.protectionLevel)); - } - if (DEBUG_SETTINGS) Log.v(TAG, - "Writing perm: name=" + bp.name + " type=" + bp.type); - if (bp.type == BasePermission.TYPE_DYNAMIC) { - PermissionInfo pi = bp.perm != null ? bp.perm.info - : bp.pendingInfo; - if (pi != null) { - serializer.attribute(null, "type", "dynamic"); - if (pi.icon != 0) { - serializer.attribute(null, "icon", - Integer.toString(pi.icon)); - } - if (pi.nonLocalizedLabel != null) { - serializer.attribute(null, "label", - pi.nonLocalizedLabel.toString()); - } - } - } - serializer.endTag(null, "item"); - } - } - - String getReadMessagesLP() { - return mReadMessages.toString(); } - - ArrayList<PackageSetting> getListOfIncompleteInstallPackages() { - HashSet<String> kList = new HashSet<String>(mPackages.keySet()); - Iterator<String> its = kList.iterator(); - ArrayList<PackageSetting> ret = new ArrayList<PackageSetting>(); - while(its.hasNext()) { - String key = its.next(); - PackageSetting ps = mPackages.get(key); - if(ps.getInstallStatus() == PKG_INSTALL_INCOMPLETE) { - ret.add(ps); - } - } - return ret; + // Process packages with valid entries. + if (mediaStatus) { + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Loading packages"); + loadMediaPackages(processCids, uidArr, removeCids); + startCleaningPackages(); + } else { + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Unloading packages"); + unloadMediaPackages(processCids, uidArr, reportStatus); } + } - boolean readLP() { - FileInputStream str = null; - if (mBackupSettingsFilename.exists()) { - try { - str = new FileInputStream(mBackupSettingsFilename); - mReadMessages.append("Reading from backup settings file\n"); - reportSettingsProblem(Log.INFO, "Need to read from backup settings file"); - if (mSettingsFilename.exists()) { - // If both the backup and settings file exist, we - // ignore the settings since it might have been - // corrupted. - Slog.w(TAG, "Cleaning up settings file " + mSettingsFilename); - mSettingsFilename.delete(); - } - } catch (java.io.IOException e) { - // We'll try for the normal settings file. - } - } - - mPastSignatures.clear(); - - try { - if (str == null) { - if (!mSettingsFilename.exists()) { - mReadMessages.append("No settings file found\n"); - reportSettingsProblem(Log.INFO, "No settings file; creating initial state"); - return false; - } - str = new FileInputStream(mSettingsFilename); - } - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(str, null); - - int type; - while ((type=parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - ; - } - - if (type != XmlPullParser.START_TAG) { - mReadMessages.append("No start tag found in settings file\n"); - reportSettingsProblem(Log.WARN, "No start tag found in package manager settings"); - Log.wtf(TAG, "No start tag found in package manager settings"); - return false; - } - - int outerDepth = parser.getDepth(); - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals("package")) { - readPackageLP(parser); - } else if (tagName.equals("permissions")) { - readPermissionsLP(mPermissions, parser); - } else if (tagName.equals("permission-trees")) { - readPermissionsLP(mPermissionTrees, parser); - } else if (tagName.equals("shared-user")) { - readSharedUserLP(parser); - } else if (tagName.equals("preferred-packages")) { - // no longer used. - } else if (tagName.equals("preferred-activities")) { - readPreferredActivitiesLP(parser); - } else if(tagName.equals("updated-package")) { - readDisabledSysPackageLP(parser); - } else if (tagName.equals("cleaning-package")) { - String name = parser.getAttributeValue(null, "name"); - if (name != null) { - mPackagesToBeCleaned.add(name); - } - } else if (tagName.equals("renamed-package")) { - String nname = parser.getAttributeValue(null, "new"); - String oname = parser.getAttributeValue(null, "old"); - if (nname != null && oname != null) { - mRenamedPackages.put(nname, oname); - } - } else if (tagName.equals("last-platform-version")) { - mInternalSdkPlatform = mExternalSdkPlatform = 0; - try { - String internal = parser.getAttributeValue(null, "internal"); - if (internal != null) { - mInternalSdkPlatform = Integer.parseInt(internal); - } - String external = parser.getAttributeValue(null, "external"); - if (external != null) { - mExternalSdkPlatform = Integer.parseInt(external); - } - } catch (NumberFormatException e) { - } - } else { - Slog.w(TAG, "Unknown element under <packages>: " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } - } - - str.close(); - - } catch(XmlPullParserException e) { - mReadMessages.append("Error reading: " + e.toString()); - reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); - Log.wtf(TAG, "Error reading package manager settings", e); - - } catch(java.io.IOException e) { - mReadMessages.append("Error reading: " + e.toString()); - reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); - Log.wtf(TAG, "Error reading package manager settings", e); - - } - - int N = mPendingPackages.size(); - for (int i=0; i<N; i++) { - final PendingPackage pp = mPendingPackages.get(i); - Object idObj = getUserIdLP(pp.sharedId); - if (idObj != null && idObj instanceof SharedUserSetting) { - PackageSetting p = getPackageLP(pp.name, null, pp.realName, - (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, - pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true); - if (p == null) { - reportSettingsProblem(Log.WARN, "Unable to create application package for " - + pp.name); - continue; - } - p.copyFrom(pp); - } else if (idObj != null) { - String msg = "Bad package setting: package " + pp.name - + " has shared uid " + pp.sharedId - + " that is not a shared uid\n"; - mReadMessages.append(msg); - reportSettingsProblem(Log.ERROR, msg); - } else { - String msg = "Bad package setting: package " + pp.name - + " has shared uid " + pp.sharedId - + " that is not defined\n"; - mReadMessages.append(msg); - reportSettingsProblem(Log.ERROR, msg); - } + private void sendResourcesChangedBroadcast(boolean mediaStatus, ArrayList<String> pkgList, + int uidArr[], IIntentReceiver finishedReceiver) { + int size = pkgList.size(); + if (size > 0) { + // Send broadcasts here + Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList + .toArray(new String[size])); + if (uidArr != null) { + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr); } - mPendingPackages.clear(); - - readStoppedLP(); - - mReadMessages.append("Read completed successfully: " - + mPackages.size() + " packages, " - + mSharedUsers.size() + " shared uids\n"); - - return true; + String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE + : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; + sendPackageBroadcast(action, null, extras, null, finishedReceiver); } + } - private int readInt(XmlPullParser parser, String ns, String name, - int defValue) { - String v = parser.getAttributeValue(ns, name); + /* + * Look at potentially valid container ids from processCids If package + * information doesn't match the one on record or package scanning fails, + * the cid is added to list of removeCids. We currently don't delete stale + * containers. + */ + private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[], + HashSet<String> removeCids) { + ArrayList<String> pkgList = new ArrayList<String>(); + Set<SdInstallArgs> keys = processCids.keySet(); + boolean doGc = false; + for (SdInstallArgs args : keys) { + String codePath = processCids.get(args); + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Loading container : " + args.cid); + int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR; try { - if (v == null) { - return defValue; - } - return Integer.parseInt(v); - } catch (NumberFormatException e) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: attribute " + - name + " has bad integer value " + v + " at " - + parser.getPositionDescription()); - } - return defValue; - } - - private void readPermissionsLP(HashMap<String, BasePermission> out, - XmlPullParser parser) - throws IOException, XmlPullParserException { - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { + // Make sure there are no container errors first. + if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) { + Slog.e(TAG, "Failed to mount cid : " + args.cid + + " when installing from sdcard"); continue; } - - String tagName = parser.getName(); - if (tagName.equals("item")) { - String name = parser.getAttributeValue(null, "name"); - String sourcePackage = parser.getAttributeValue(null, "package"); - String ptype = parser.getAttributeValue(null, "type"); - if (name != null && sourcePackage != null) { - boolean dynamic = "dynamic".equals(ptype); - BasePermission bp = new BasePermission(name, sourcePackage, - dynamic - ? BasePermission.TYPE_DYNAMIC - : BasePermission.TYPE_NORMAL); - bp.protectionLevel = readInt(parser, null, "protection", - PermissionInfo.PROTECTION_NORMAL); - if (dynamic) { - PermissionInfo pi = new PermissionInfo(); - pi.packageName = sourcePackage.intern(); - pi.name = name.intern(); - pi.icon = readInt(parser, null, "icon", 0); - pi.nonLocalizedLabel = parser.getAttributeValue( - null, "label"); - pi.protectionLevel = bp.protectionLevel; - bp.pendingInfo = pi; - } - out.put(bp.name, bp); - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: permissions has" - + " no name at " + parser.getPositionDescription()); - } - } else { - reportSettingsProblem(Log.WARN, - "Unknown element reading permissions: " - + parser.getName() + " at " - + parser.getPositionDescription()); - } - XmlUtils.skipCurrentTag(parser); - } - } - - private void readDisabledSysPackageLP(XmlPullParser parser) - throws XmlPullParserException, IOException { - String name = parser.getAttributeValue(null, "name"); - String realName = parser.getAttributeValue(null, "realName"); - String codePathStr = parser.getAttributeValue(null, "codePath"); - String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); - String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - if (resourcePathStr == null) { - resourcePathStr = codePathStr; - } - String version = parser.getAttributeValue(null, "version"); - int versionCode = 0; - if (version != null) { - try { - versionCode = Integer.parseInt(version); - } catch (NumberFormatException e) { - } - } - - int pkgFlags = 0; - pkgFlags |= ApplicationInfo.FLAG_SYSTEM; - PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), - new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags); - String timeStampStr = parser.getAttributeValue(null, "ft"); - if (timeStampStr != null) { - try { - long timeStamp = Long.parseLong(timeStampStr, 16); - ps.setTimeStamp(timeStamp); - } catch (NumberFormatException e) { - } - } else { - timeStampStr = parser.getAttributeValue(null, "ts"); - if (timeStampStr != null) { - try { - long timeStamp = Long.parseLong(timeStampStr); - ps.setTimeStamp(timeStamp); - } catch (NumberFormatException e) { - } - } - } - timeStampStr = parser.getAttributeValue(null, "it"); - if (timeStampStr != null) { - try { - ps.firstInstallTime = Long.parseLong(timeStampStr, 16); - } catch (NumberFormatException e) { - } - } - timeStampStr = parser.getAttributeValue(null, "ut"); - if (timeStampStr != null) { - try { - ps.lastUpdateTime = Long.parseLong(timeStampStr, 16); - } catch (NumberFormatException e) { - } - } - String idStr = parser.getAttributeValue(null, "userId"); - ps.userId = idStr != null ? Integer.parseInt(idStr) : 0; - if(ps.userId <= 0) { - String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); - ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; - } - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { + // Check code path here. + if (codePath == null || !codePath.equals(args.getCodePath())) { + Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath() + + " does not match one in settings " + codePath); continue; } - - String tagName = parser.getName(); - if (tagName.equals("perms")) { - readGrantedPermissionsLP(parser, - ps.grantedPermissions); - } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <updated-package>: " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } - } - mDisabledSysPackages.put(name, ps); - } - - private void readPackageLP(XmlPullParser parser) - throws XmlPullParserException, IOException { - String name = null; - String realName = null; - String idStr = null; - String sharedIdStr = null; - String codePathStr = null; - String resourcePathStr = null; - String nativeLibraryPathStr = null; - String systemStr = null; - String installerPackageName = null; - String uidError = null; - int pkgFlags = 0; - long timeStamp = 0; - long firstInstallTime = 0; - long lastUpdateTime = 0; - PackageSettingBase packageSetting = null; - String version = null; - int versionCode = 0; - try { - name = parser.getAttributeValue(null, "name"); - realName = parser.getAttributeValue(null, "realName"); - idStr = parser.getAttributeValue(null, "userId"); - uidError = parser.getAttributeValue(null, "uidError"); - sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); - codePathStr = parser.getAttributeValue(null, "codePath"); - resourcePathStr = parser.getAttributeValue(null, "resourcePath"); - nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - version = parser.getAttributeValue(null, "version"); - if (version != null) { - try { - versionCode = Integer.parseInt(version); - } catch (NumberFormatException e) { - } - } - installerPackageName = parser.getAttributeValue(null, "installer"); - - systemStr = parser.getAttributeValue(null, "flags"); - if (systemStr != null) { - try { - pkgFlags = Integer.parseInt(systemStr); - } catch (NumberFormatException e) { - } - } else { - // For backward compatibility - systemStr = parser.getAttributeValue(null, "system"); - if (systemStr != null) { - pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM : 0; - } else { - // Old settings that don't specify system... just treat - // them as system, good enough. - pkgFlags |= ApplicationInfo.FLAG_SYSTEM; - } - } - String timeStampStr = parser.getAttributeValue(null, "ft"); - if (timeStampStr != null) { - try { - timeStamp = Long.parseLong(timeStampStr, 16); - } catch (NumberFormatException e) { - } - } else { - timeStampStr = parser.getAttributeValue(null, "ts"); - if (timeStampStr != null) { - try { - timeStamp = Long.parseLong(timeStampStr); - } catch (NumberFormatException e) { + // Parse package + int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags; + doGc = true; + synchronized (mInstallLock) { + final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags, + 0, 0); + // Scan the package + if (pkg != null) { + /* + * TODO why is the lock being held? doPostInstall is + * called in other places without the lock. This needs + * to be straightened out. + */ + // writer + synchronized (mPackages) { + retCode = PackageManager.INSTALL_SUCCEEDED; + pkgList.add(pkg.packageName); + // Post process args + args.doPostInstall(PackageManager.INSTALL_SUCCEEDED); } - } - } - timeStampStr = parser.getAttributeValue(null, "it"); - if (timeStampStr != null) { - try { - firstInstallTime = Long.parseLong(timeStampStr, 16); - } catch (NumberFormatException e) { - } - } - timeStampStr = parser.getAttributeValue(null, "ut"); - if (timeStampStr != null) { - try { - lastUpdateTime = Long.parseLong(timeStampStr, 16); - } catch (NumberFormatException e) { - } - } - if (DEBUG_SETTINGS) Log.v(TAG, "Reading package: " + name - + " userId=" + idStr + " sharedUserId=" + sharedIdStr); - int userId = idStr != null ? Integer.parseInt(idStr) : 0; - if (resourcePathStr == null) { - resourcePathStr = codePathStr; - } - if (realName != null) { - realName = realName.intern(); - } - if (name == null) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <package> has no name at " - + parser.getPositionDescription()); - } else if (codePathStr == null) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <package> has no codePath at " - + parser.getPositionDescription()); - } else if (userId > 0) { - packageSetting = addPackageLP(name.intern(), realName, new File(codePathStr), - new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode, - pkgFlags); - if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name - + ": userId=" + userId + " pkg=" + packageSetting); - if (packageSetting == null) { - reportSettingsProblem(Log.ERROR, - "Failure adding uid " + userId - + " while parsing settings at " - + parser.getPositionDescription()); - } else { - packageSetting.setTimeStamp(timeStamp); - packageSetting.firstInstallTime = firstInstallTime; - packageSetting.lastUpdateTime = lastUpdateTime; - } - } else if (sharedIdStr != null) { - userId = sharedIdStr != null - ? Integer.parseInt(sharedIdStr) : 0; - if (userId > 0) { - packageSetting = new PendingPackage(name.intern(), realName, - new File(codePathStr), new File(resourcePathStr), - nativeLibraryPathStr, userId, versionCode, pkgFlags); - packageSetting.setTimeStamp(timeStamp); - packageSetting.firstInstallTime = firstInstallTime; - packageSetting.lastUpdateTime = lastUpdateTime; - mPendingPackages.add((PendingPackage) packageSetting); - if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name - + ": sharedUserId=" + userId + " pkg=" - + packageSetting); - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: package " - + name + " has bad sharedId " + sharedIdStr - + " at " + parser.getPositionDescription()); - } - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: package " - + name + " has bad userId " + idStr + " at " - + parser.getPositionDescription()); - } - } catch (NumberFormatException e) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: package " - + name + " has bad userId " + idStr + " at " - + parser.getPositionDescription()); - } - if (packageSetting != null) { - packageSetting.uidError = "true".equals(uidError); - packageSetting.installerPackageName = installerPackageName; - packageSetting.nativeLibraryPathString = nativeLibraryPathStr; - final String enabledStr = parser.getAttributeValue(null, "enabled"); - if (enabledStr != null) { - if (enabledStr.equalsIgnoreCase("true")) { - packageSetting.enabled = COMPONENT_ENABLED_STATE_ENABLED; - } else if (enabledStr.equalsIgnoreCase("false")) { - packageSetting.enabled = COMPONENT_ENABLED_STATE_DISABLED; - } else if (enabledStr.equalsIgnoreCase("default")) { - packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT; - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: package " - + name + " has bad enabled value: " + idStr - + " at " + parser.getPositionDescription()); - } - } else { - packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT; - } - final String installStatusStr = parser.getAttributeValue(null, "installStatus"); - if (installStatusStr != null) { - if (installStatusStr.equalsIgnoreCase("false")) { - packageSetting.installStatus = PKG_INSTALL_INCOMPLETE; } else { - packageSetting.installStatus = PKG_INSTALL_COMPLETE; + Slog.i(TAG, "Failed to install pkg from " + codePath + " from sdcard"); } } - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals("disabled-components")) { - readDisabledComponentsLP(packageSetting, parser); - } else if (tagName.equals("enabled-components")) { - readEnabledComponentsLP(packageSetting, parser); - } else if (tagName.equals("sigs")) { - packageSetting.signatures.readXml(parser, mPastSignatures); - } else if (tagName.equals("perms")) { - readGrantedPermissionsLP(parser, - packageSetting.grantedPermissions); - packageSetting.permissionsFixed = true; - } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <package>: " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } + } finally { + if (retCode != PackageManager.INSTALL_SUCCEEDED) { + // Don't destroy container here. Wait till gc clears things + // up. + removeCids.add(args.cid); } - } else { - XmlUtils.skipCurrentTag(parser); } } - - private void readDisabledComponentsLP(PackageSettingBase packageSetting, - XmlPullParser parser) - throws IOException, XmlPullParserException { - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals("item")) { - String name = parser.getAttributeValue(null, "name"); - if (name != null) { - packageSetting.disabledComponents.add(name.intern()); - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <disabled-components> has" - + " no name at " + parser.getPositionDescription()); - } + // writer + synchronized (mPackages) { + // If the platform SDK has changed since the last time we booted, + // we need to re-grant app permission to catch any new ones that + // appear. This is really a hack, and means that apps can in some + // cases get permissions that the user didn't initially explicitly + // allow... it would be nice to have some better way to handle + // this situation. + final boolean regrantPermissions = mSettings.mExternalSdkPlatform != mSdkVersion; + if (regrantPermissions) + Slog.i(TAG, "Platform changed from " + mSettings.mExternalSdkPlatform + " to " + + mSdkVersion + "; regranting permissions for external storage"); + mSettings.mExternalSdkPlatform = mSdkVersion; + + // Make sure group IDs have been assigned, and any permission + // changes in other apps are accounted for + updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions); + // can downgrade to reader + // Persist settings + mSettings.writeLPr(); + } + // Send a broadcast to let everyone know we are done processing + if (pkgList.size() > 0) { + sendResourcesChangedBroadcast(true, pkgList, uidArr, null); + } + // Force gc to avoid any stale parser references that we might have. + if (doGc) { + Runtime.getRuntime().gc(); + } + // List stale containers and destroy stale temporary containers. + if (removeCids != null) { + for (String cid : removeCids) { + if (cid.startsWith(mTempContainerPrefix)) { + Log.i(TAG, "Destroying stale temporary container " + cid); + PackageHelper.destroySdDir(cid); } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <disabled-components>: " - + parser.getName()); - } - XmlUtils.skipCurrentTag(parser); - } + Log.w(TAG, "Container " + cid + " is stale"); + } + } } + } - private void readEnabledComponentsLP(PackageSettingBase packageSetting, - XmlPullParser parser) - throws IOException, XmlPullParserException { - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { - continue; - } + /* + * Utility method to unload a list of specified containers + */ + private void unloadAllContainers(Set<SdInstallArgs> cidArgs) { + // Just unmount all valid containers. + for (SdInstallArgs arg : cidArgs) { + synchronized (mInstallLock) { + arg.doPostDeleteLI(false); + } + } + } - String tagName = parser.getName(); - if (tagName.equals("item")) { - String name = parser.getAttributeValue(null, "name"); - if (name != null) { - packageSetting.enabledComponents.add(name.intern()); - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <enabled-components> has" - + " no name at " + parser.getPositionDescription()); - } + /* + * Unload packages mounted on external media. This involves deleting package + * data from internal structures, sending broadcasts about diabled packages, + * gc'ing to free up references, unmounting all secure containers + * corresponding to packages on external media, and posting a + * UPDATED_MEDIA_STATUS message if status has been requested. Please note + * that we always have to post this message if status has been requested no + * matter what. + */ + private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[], + final boolean reportStatus) { + if (DEBUG_SD_INSTALL) + Log.i(TAG, "unloading media packages"); + ArrayList<String> pkgList = new ArrayList<String>(); + ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>(); + final Set<SdInstallArgs> keys = processCids.keySet(); + for (SdInstallArgs args : keys) { + String pkgName = args.getPackageName(); + if (DEBUG_SD_INSTALL) + Log.i(TAG, "Trying to unload pkg : " + pkgName); + // Delete package internally + PackageRemovedInfo outInfo = new PackageRemovedInfo(); + synchronized (mInstallLock) { + boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, + outInfo, false); + if (res) { + pkgList.add(pkgName); } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <enabled-components>: " - + parser.getName()); + Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName); + failedList.add(args); } - XmlUtils.skipCurrentTag(parser); } } - private void readSharedUserLP(XmlPullParser parser) - throws XmlPullParserException, IOException { - String name = null; - String idStr = null; - int pkgFlags = 0; - SharedUserSetting su = null; - try { - name = parser.getAttributeValue(null, "name"); - idStr = parser.getAttributeValue(null, "userId"); - int userId = idStr != null ? Integer.parseInt(idStr) : 0; - if ("true".equals(parser.getAttributeValue(null, "system"))) { - pkgFlags |= ApplicationInfo.FLAG_SYSTEM; - } - if (name == null) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <shared-user> has no name at " - + parser.getPositionDescription()); - } else if (userId == 0) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: shared-user " - + name + " has bad userId " + idStr + " at " - + parser.getPositionDescription()); - } else { - if ((su=addSharedUserLP(name.intern(), userId, pkgFlags)) == null) { - reportSettingsProblem(Log.ERROR, - "Occurred while parsing settings at " - + parser.getPositionDescription()); - } - } - } catch (NumberFormatException e) { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: package " - + name + " has bad userId " + idStr + " at " - + parser.getPositionDescription()); - }; - - if (su != null) { - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals("sigs")) { - su.signatures.readXml(parser, mPastSignatures); - } else if (tagName.equals("perms")) { - readGrantedPermissionsLP(parser, su.grantedPermissions); - } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <shared-user>: " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } + // reader + synchronized (mPackages) { + // We didn't update the settings after removing each package; + // write them now for all packages. + mSettings.writeLPr(); + } + + // We have to absolutely send UPDATED_MEDIA_STATUS only + // after confirming that all the receivers processed the ordered + // broadcast when packages get disabled, force a gc to clean things up. + // and unload all the containers. + if (pkgList.size() > 0) { + sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() { + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky) throws RemoteException { + Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, + reportStatus ? 1 : 0, 1, keys); + mHandler.sendMessage(msg); } - - } else { - XmlUtils.skipCurrentTag(parser); - } + }); + } else { + Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 : 0, -1, + keys); + mHandler.sendMessage(msg); } + } - private void readGrantedPermissionsLP(XmlPullParser parser, - HashSet<String> outPerms) throws IOException, XmlPullParserException { - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals("item")) { - String name = parser.getAttributeValue(null, "name"); - if (name != null) { - outPerms.add(name.intern()); - } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <perms> has" - + " no name at " + parser.getPositionDescription()); - } + public void movePackage(final String packageName, final IPackageMoveObserver observer, + final int flags) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); + int returnCode = PackageManager.MOVE_SUCCEEDED; + int currFlags = 0; + int newFlags = 0; + // reader + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST; + } else { + // Disable moving fwd locked apps and system packages + if (pkg.applicationInfo != null && isSystemApp(pkg)) { + Slog.w(TAG, "Cannot move system application"); + returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; + } else if (pkg.applicationInfo != null && isForwardLocked(pkg)) { + Slog.w(TAG, "Cannot move forward locked app."); + returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED; + } else if (pkg.mOperationPending) { + Slog.w(TAG, "Attempt to move package which has pending operations"); + returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING; } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <perms>: " - + parser.getName()); - } - XmlUtils.skipCurrentTag(parser); - } - } - - private void readPreferredActivitiesLP(XmlPullParser parser) - throws XmlPullParserException, IOException { - int outerDepth = parser.getDepth(); - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG - || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals("item")) { - PreferredActivity pa = new PreferredActivity(parser); - if (pa.mPref.getParseError() == null) { - mPreferredActivities.addFilter(pa); + // Find install location first + if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 + && (flags & PackageManager.MOVE_INTERNAL) != 0) { + Slog.w(TAG, "Ambigous flags specified for move location."); + returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION; } else { - reportSettingsProblem(Log.WARN, - "Error in package manager settings: <preferred-activity> " - + pa.mPref.getParseError() + " at " - + parser.getPositionDescription()); + newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ? PackageManager.INSTALL_EXTERNAL + : PackageManager.INSTALL_INTERNAL; + currFlags = isExternal(pkg) ? PackageManager.INSTALL_EXTERNAL + : PackageManager.INSTALL_INTERNAL; + if (newFlags == currFlags) { + Slog.w(TAG, "No move required. Trying to move to same location"); + returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION; + } } - } else { - reportSettingsProblem(Log.WARN, - "Unknown element under <preferred-activities>: " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); - } - } - } - - // Returns -1 if we could not find an available UserId to assign - private int newUserIdLP(Object obj) { - // Let's be stupidly inefficient for now... - final int N = mUserIds.size(); - for (int i=0; i<N; i++) { - if (mUserIds.get(i) == null) { - mUserIds.set(i, obj); - return FIRST_APPLICATION_UID + i; - } - } - - // None left? - if (N >= MAX_APPLICATION_UIDS) { - return -1; - } - - mUserIds.add(obj); - return FIRST_APPLICATION_UID + N; - } - - public PackageSetting getDisabledSystemPkg(String name) { - synchronized(mPackages) { - PackageSetting ps = mDisabledSysPackages.get(name); - return ps; - } - } - - boolean isEnabledLP(ComponentInfo componentInfo, int flags) { - if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - return true; - } - final PackageSetting packageSettings = mPackages.get(componentInfo.packageName); - if (Config.LOGV) { - Log.v(TAG, "isEnabledLock - packageName = " + componentInfo.packageName - + " componentName = " + componentInfo.name); - Log.v(TAG, "enabledComponents: " - + Arrays.toString(packageSettings.enabledComponents.toArray())); - Log.v(TAG, "disabledComponents: " - + Arrays.toString(packageSettings.disabledComponents.toArray())); - } - if (packageSettings == null) { - if (false) { - Log.w(TAG, "WAITING FOR DEBUGGER"); - Debug.waitForDebugger(); - Log.i(TAG, "We will crash!"); - } - return false; - } - if (packageSettings.enabled == COMPONENT_ENABLED_STATE_DISABLED - || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled - && packageSettings.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) { - return false; - } - if (packageSettings.enabledComponents.contains(componentInfo.name)) { - return true; - } - if (packageSettings.disabledComponents.contains(componentInfo.name)) { - return false; - } - return componentInfo.enabled; - } - } - - // ------- apps on sdcard specific code ------- - static final boolean DEBUG_SD_INSTALL = false; - private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD"; - private static final String SD_ENCRYPTION_ALGORITHM = "AES"; - static final int MAX_CONTAINERS = 250; - private boolean mMediaMounted = false; - - private String getEncryptKey() { - try { - String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString( - SD_ENCRYPTION_KEYSTORE_NAME); - if (sdEncKey == null) { - sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128, - SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME); - if (sdEncKey == null) { - Slog.e(TAG, "Failed to create encryption keys"); - return null; - } - } - return sdEncKey; - } catch (NoSuchAlgorithmException nsae) { - Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae); - return null; - } catch (IOException ioe) { - Slog.e(TAG, "Failed to retrieve encryption keys with exception: " - + ioe); - return null; - } - - } - - /* package */ static String getTempContainerId() { - int tmpIdx = 1; - String list[] = PackageHelper.getSecureContainerList(); - if (list != null) { - for (final String name : list) { - // Ignore null and non-temporary container entries - if (name == null || !name.startsWith(mTempContainerPrefix)) { - continue; - } - - String subStr = name.substring(mTempContainerPrefix.length()); - try { - int cid = Integer.parseInt(subStr); - if (cid >= tmpIdx) { - tmpIdx = cid + 1; + if (returnCode == PackageManager.MOVE_SUCCEEDED) { + pkg.mOperationPending = true; } - } catch (NumberFormatException e) { } } - } - return mTempContainerPrefix + tmpIdx; - } - - /* - * Update media status on PackageManager. - */ - public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) { - int callingUid = Binder.getCallingUid(); - if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { - throw new SecurityException("Media status can only be updated by the system"); - } - synchronized (mPackages) { - Log.i(TAG, "Updating external media status from " + - (mMediaMounted ? "mounted" : "unmounted") + " to " + - (mediaStatus ? "mounted" : "unmounted")); - if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + - mediaStatus+", mMediaMounted=" + mMediaMounted); - if (mediaStatus == mMediaMounted) { - Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, - reportStatus ? 1 : 0, -1); - mHandler.sendMessage(msg); - return; - } - mMediaMounted = mediaStatus; - } - // Queue up an async operation since the package installation may take a little while. - mHandler.post(new Runnable() { - public void run() { - mHandler.removeCallbacks(this); - updateExternalMediaStatusInner(mediaStatus, reportStatus); - } - }); - } - /* - * Collect information of applications on external media, map them - * against existing containers and update information based on current - * mount status. Please note that we always have to report status - * if reportStatus has been set to true especially when unloading packages. - */ - private void updateExternalMediaStatusInner(boolean mediaStatus, - boolean reportStatus) { - // Collection of uids - int uidArr[] = null; - // Collection of stale containers - HashSet<String> removeCids = new HashSet<String>(); - // Collection of packages on external media with valid containers. - HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>(); - // Get list of secure containers. - final String list[] = PackageHelper.getSecureContainerList(); - if (list == null || list.length == 0) { - Log.i(TAG, "No secure containers on sdcard"); - } else { - // Process list of secure containers and categorize them - // as active or stale based on their package internal state. - int uidList[] = new int[list.length]; - int num = 0; - synchronized (mPackages) { - for (String cid : list) { - SdInstallArgs args = new SdInstallArgs(cid); - if (DEBUG_SD_INSTALL) Log.i(TAG, "Processing container " + cid); - String pkgName = args.getPackageName(); - if (pkgName == null) { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale"); - removeCids.add(cid); - continue; - } - if (DEBUG_SD_INSTALL) Log.i(TAG, "Looking for pkg : " + pkgName); - PackageSetting ps = mSettings.mPackages.get(pkgName); - // The package status is changed only if the code path - // matches between settings and the container id. - if (ps != null && ps.codePathString != null && - ps.codePathString.equals(args.getCodePath())) { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + - " corresponds to pkg : " + pkgName + - " at code path: " + ps.codePathString); - // We do have a valid package installed on sdcard - processCids.put(args, ps.codePathString); - int uid = ps.userId; - if (uid != -1) { - uidList[num++] = uid; - } - } else { - // Stale container on sdcard. Just delete - if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale"); - removeCids.add(cid); - } - } - } - - if (num > 0) { - // Sort uid list - Arrays.sort(uidList, 0, num); - // Throw away duplicates - uidArr = new int[num]; - uidArr[0] = uidList[0]; - int di = 0; - for (int i = 1; i < num; i++) { - if (uidList[i-1] != uidList[i]) { - uidArr[di++] = uidList[i]; - } - } - } - } - // Process packages with valid entries. - if (mediaStatus) { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); - loadMediaPackages(processCids, uidArr, removeCids); - startCleaningPackages(); - } else { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); - unloadMediaPackages(processCids, uidArr, reportStatus); - } - } - - private void sendResourcesChangedBroadcast(boolean mediaStatus, - ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) { - int size = pkgList.size(); - if (size > 0) { - // Send broadcasts here - Bundle extras = new Bundle(); - extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, - pkgList.toArray(new String[size])); - if (uidArr != null) { - extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr); - } - String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE - : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; - sendPackageBroadcast(action, null, extras, null, finishedReceiver); - } - } - - /* - * Look at potentially valid container ids from processCids - * If package information doesn't match the one on record - * or package scanning fails, the cid is added to list of - * removeCids. We currently don't delete stale containers. - */ - private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, - int uidArr[], HashSet<String> removeCids) { - ArrayList<String> pkgList = new ArrayList<String>(); - Set<SdInstallArgs> keys = processCids.keySet(); - boolean doGc = false; - for (SdInstallArgs args : keys) { - String codePath = processCids.get(args); - if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading container : " - + args.cid); - int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR; - try { - // Make sure there are no container errors first. - if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) - != PackageManager.INSTALL_SUCCEEDED) { - Slog.e(TAG, "Failed to mount cid : " + args.cid + - " when installing from sdcard"); - continue; - } - // Check code path here. - if (codePath == null || !codePath.equals(args.getCodePath())) { - Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()+ - " does not match one in settings " + codePath); - continue; - } - // Parse package - int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags; - doGc = true; - synchronized (mInstallLock) { - final PackageParser.Package pkg = scanPackageLI(new File(codePath), - parseFlags, 0, 0); - // Scan the package - if (pkg != null) { - synchronized (mPackages) { - retCode = PackageManager.INSTALL_SUCCEEDED; - pkgList.add(pkg.packageName); - // Post process args - args.doPostInstall(PackageManager.INSTALL_SUCCEEDED); - } - } else { - Slog.i(TAG, "Failed to install pkg from " + - codePath + " from sdcard"); - } - } - - } finally { - if (retCode != PackageManager.INSTALL_SUCCEEDED) { - // Don't destroy container here. Wait till gc clears things up. - removeCids.add(args.cid); - } - } - } - synchronized (mPackages) { - // If the platform SDK has changed since the last time we booted, - // we need to re-grant app permission to catch any new ones that - // appear. This is really a hack, and means that apps can in some - // cases get permissions that the user didn't initially explicitly - // allow... it would be nice to have some better way to handle - // this situation. - final boolean regrantPermissions = mSettings.mExternalSdkPlatform - != mSdkVersion; - if (regrantPermissions) Slog.i(TAG, "Platform changed from " - + mSettings.mExternalSdkPlatform + " to " + mSdkVersion - + "; regranting permissions for external storage"); - mSettings.mExternalSdkPlatform = mSdkVersion; - - // Make sure group IDs have been assigned, and any permission - // changes in other apps are accounted for - updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions); - // Persist settings - mSettings.writeLP(); - } - // Send a broadcast to let everyone know we are done processing - if (pkgList.size() > 0) { - sendResourcesChangedBroadcast(true, pkgList, uidArr, null); - } - // Force gc to avoid any stale parser references that we might have. - if (doGc) { - Runtime.getRuntime().gc(); - } - // List stale containers and destroy stale temporary containers. - if (removeCids != null) { - for (String cid : removeCids) { - if (cid.startsWith(mTempContainerPrefix)) { - Log.i(TAG, "Destroying stale temporary container " + cid); - PackageHelper.destroySdDir(cid); - } else { - Log.w(TAG, "Container " + cid + " is stale"); - } - } - } - } - - /* - * Utility method to unload a list of specified containers - */ - private void unloadAllContainers(Set<SdInstallArgs> cidArgs) { - // Just unmount all valid containers. - for (SdInstallArgs arg : cidArgs) { - synchronized (mInstallLock) { - arg.doPostDeleteLI(false); - } - } - } - - /* - * Unload packages mounted on external media. This involves deleting - * package data from internal structures, sending broadcasts about - * diabled packages, gc'ing to free up references, unmounting all - * secure containers corresponding to packages on external media, and - * posting a UPDATED_MEDIA_STATUS message if status has been requested. - * Please note that we always have to post this message if status has - * been requested no matter what. - */ - private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, - int uidArr[], final boolean reportStatus) { - if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages"); - ArrayList<String> pkgList = new ArrayList<String>(); - ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>(); - final Set<SdInstallArgs> keys = processCids.keySet(); - for (SdInstallArgs args : keys) { - String cid = args.cid; - String pkgName = args.getPackageName(); - if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName); - // Delete package internally - PackageRemovedInfo outInfo = new PackageRemovedInfo(); - synchronized (mInstallLock) { - boolean res = deletePackageLI(pkgName, false, - PackageManager.DONT_DELETE_DATA, outInfo, false); - if (res) { - pkgList.add(pkgName); - } else { - Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName); - failedList.add(args); - } - } - } - - synchronized (mPackages) { - // We didn't update the settings after removing each package; - // write them now for all packages. - mSettings.writeLP(); - } - - // We have to absolutely send UPDATED_MEDIA_STATUS only - // after confirming that all the receivers processed the ordered - // broadcast when packages get disabled, force a gc to clean things up. - // and unload all the containers. - if (pkgList.size() > 0) { - sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() { - public void performReceive(Intent intent, int resultCode, String data, Bundle extras, - boolean ordered, boolean sticky) throws RemoteException { - Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, - reportStatus ? 1 : 0, 1, keys); - mHandler.sendMessage(msg); - } - }); - } else { - Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, - reportStatus ? 1 : 0, -1, keys); - mHandler.sendMessage(msg); - } - } - - public void movePackage(final String packageName, - final IPackageMoveObserver observer, final int flags) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MOVE_PACKAGE, null); - int returnCode = PackageManager.MOVE_SUCCEEDED; - int currFlags = 0; - int newFlags = 0; - synchronized (mPackages) { - PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { - returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST; - } else { - // Disable moving fwd locked apps and system packages - if (pkg.applicationInfo != null && isSystemApp(pkg)) { - Slog.w(TAG, "Cannot move system application"); - returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; - } else if (pkg.applicationInfo != null && isForwardLocked(pkg)) { - Slog.w(TAG, "Cannot move forward locked app."); - returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED; - } else if (pkg.mOperationPending) { - Slog.w(TAG, "Attempt to move package which has pending operations"); - returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING; - } else { - // Find install location first - if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 && - (flags & PackageManager.MOVE_INTERNAL) != 0) { - Slog.w(TAG, "Ambigous flags specified for move location."); - returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION; - } else { - newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ? - PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL; - currFlags = isExternal(pkg) ? PackageManager.INSTALL_EXTERNAL - : PackageManager.INSTALL_INTERNAL; - if (newFlags == currFlags) { - Slog.w(TAG, "No move required. Trying to move to same location"); - returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION; - } - } - if (returnCode == PackageManager.MOVE_SUCCEEDED) { - pkg.mOperationPending = true; - } - } - } - if (returnCode != PackageManager.MOVE_SUCCEEDED) { + /* + * TODO this next block probably shouldn't be inside the lock. We + * can't guarantee these won't change after this is fired off + * anyway. + */ + if (returnCode != PackageManager.MOVE_SUCCEEDED) { processPendingMove(new MoveParams(null, observer, 0, packageName, null), returnCode); - } else { - Message msg = mHandler.obtainMessage(INIT_COPY); - InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, + } else { + Message msg = mHandler.obtainMessage(INIT_COPY); + InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir); - MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, + MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, pkg.applicationInfo.dataDir); - msg.obj = mp; - mHandler.sendMessage(msg); - } - } - } + msg.obj = mp; + mHandler.sendMessage(msg); + } + } + } - private void processPendingMove(final MoveParams mp, final int currentStatus) { - // Queue up an async operation since the package deletion may take a little while. - mHandler.post(new Runnable() { - public void run() { - mHandler.removeCallbacks(this); - int returnCode = currentStatus; - if (currentStatus == PackageManager.MOVE_SUCCEEDED) { - int uidArr[] = null; - ArrayList<String> pkgList = null; - synchronized (mPackages) { - PackageParser.Package pkg = mPackages.get(mp.packageName); - if (pkg == null) { - Slog.w(TAG, " Package " + mp.packageName + - " doesn't exist. Aborting move"); - returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST; - } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) { - Slog.w(TAG, "Package " + mp.packageName + " code path changed from " + - mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir + - " Aborting move and returning error"); - returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR; - } else { - uidArr = new int[] { pkg.applicationInfo.uid }; - pkgList = new ArrayList<String>(); - pkgList.add(mp.packageName); - } - } - if (returnCode == PackageManager.MOVE_SUCCEEDED) { - // Send resources unavailable broadcast - sendResourcesChangedBroadcast(false, pkgList, uidArr, null); - // Update package code and resource paths - synchronized (mInstallLock) { - synchronized (mPackages) { - PackageParser.Package pkg = mPackages.get(mp.packageName); - // Recheck for package again. + private void processPendingMove(final MoveParams mp, final int currentStatus) { + // Queue up an async operation since the package deletion may take a + // little while. + mHandler.post(new Runnable() { + public void run() { + // TODO fix this; this does nothing. + mHandler.removeCallbacks(this); + int returnCode = currentStatus; + if (currentStatus == PackageManager.MOVE_SUCCEEDED) { + int uidArr[] = null; + ArrayList<String> pkgList = null; + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(mp.packageName); + if (pkg == null) { + Slog.w(TAG, " Package " + mp.packageName + + " doesn't exist. Aborting move"); + returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST; + } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) { + Slog.w(TAG, "Package " + mp.packageName + " code path changed from " + + mp.srcArgs.getCodePath() + " to " + + pkg.applicationInfo.sourceDir + + " Aborting move and returning error"); + returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR; + } else { + uidArr = new int[] { + pkg.applicationInfo.uid + }; + pkgList = new ArrayList<String>(); + pkgList.add(mp.packageName); + } + } + if (returnCode == PackageManager.MOVE_SUCCEEDED) { + // Send resources unavailable broadcast + sendResourcesChangedBroadcast(false, pkgList, uidArr, null); + // Update package code and resource paths + synchronized (mInstallLock) { + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(mp.packageName); + // Recheck for package again. if (pkg == null) { Slog.w(TAG, " Package " + mp.packageName + " doesn't exist. Aborting move"); returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST; - } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) { - Slog.w(TAG, "Package " + mp.packageName + " code path changed from " + - mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir + - " Aborting move and returning error"); - returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR; - } else { - final String oldCodePath = pkg.mPath; - final String newCodePath = mp.targetArgs.getCodePath(); - final String newResPath = mp.targetArgs.getResourcePath(); - final String newNativePath = mp.targetArgs.getNativeLibraryPath(); + } else if (!mp.srcArgs.getCodePath().equals( + pkg.applicationInfo.sourceDir)) { + Slog.w(TAG, "Package " + mp.packageName + + " code path changed from " + mp.srcArgs.getCodePath() + + " to " + pkg.applicationInfo.sourceDir + + " Aborting move and returning error"); + returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR; + } else { + final String oldCodePath = pkg.mPath; + final String newCodePath = mp.targetArgs.getCodePath(); + final String newResPath = mp.targetArgs.getResourcePath(); + final String newNativePath = mp.targetArgs + .getNativeLibraryPath(); if ((mp.flags & PackageManager.INSTALL_EXTERNAL) == 0) { if (mInstaller .unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) { returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; } else { - NativeLibraryHelper.copyNativeBinariesLI( - new File(newCodePath), new File(newNativePath)); + NativeLibraryHelper.copyNativeBinariesLI(new File( + newCodePath), new File(newNativePath)); } } else { if (mInstaller.linkNativeLibraryDirectory( @@ -10436,90 +7922,92 @@ class PackageManagerService extends IPackageManager.Stub { } if (returnCode == PackageManager.MOVE_SUCCEEDED) { - pkg.mScanPath = newCodePath; - pkg.applicationInfo.sourceDir = newCodePath; - pkg.applicationInfo.publicSourceDir = newResPath; - pkg.applicationInfo.nativeLibraryDir = newNativePath; - PackageSetting ps = (PackageSetting) pkg.mExtras; - ps.codePath = new File(pkg.applicationInfo.sourceDir); - ps.codePathString = ps.codePath.getPath(); - ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir); - ps.resourcePathString = ps.resourcePath.getPath(); - ps.nativeLibraryPathString = newNativePath; - // Set the application info flag correctly. - if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) { - pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; - } else { - pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE; - } - ps.setFlags(pkg.applicationInfo.flags); - mAppDirs.remove(oldCodePath); - mAppDirs.put(newCodePath, pkg); - // Persist settings - mSettings.writeLP(); - } - } - } - } - // Send resources available broadcast - sendResourcesChangedBroadcast(true, pkgList, uidArr, null); - } - } - if (returnCode != PackageManager.MOVE_SUCCEEDED){ - // Clean up failed installation - if (mp.targetArgs != null) { - mp.targetArgs.doPostInstall(PackageManager.INSTALL_FAILED_INTERNAL_ERROR); - } - } else { - // Force a gc to clear things up. - Runtime.getRuntime().gc(); - // Delete older code - synchronized (mInstallLock) { - mp.srcArgs.doPostDeleteLI(true); - } - } + pkg.mScanPath = newCodePath; + pkg.applicationInfo.sourceDir = newCodePath; + pkg.applicationInfo.publicSourceDir = newResPath; + pkg.applicationInfo.nativeLibraryDir = newNativePath; + PackageSetting ps = (PackageSetting) pkg.mExtras; + ps.codePath = new File(pkg.applicationInfo.sourceDir); + ps.codePathString = ps.codePath.getPath(); + ps.resourcePath = new File( + pkg.applicationInfo.publicSourceDir); + ps.resourcePathString = ps.resourcePath.getPath(); + ps.nativeLibraryPathString = newNativePath; + // Set the application info flag + // correctly. + if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; + } else { + pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE; + } + ps.setFlags(pkg.applicationInfo.flags); + mAppDirs.remove(oldCodePath); + mAppDirs.put(newCodePath, pkg); + // Persist settings + mSettings.writeLPr(); + } + } + } + } + // Send resources available broadcast + sendResourcesChangedBroadcast(true, pkgList, uidArr, null); + } + } + if (returnCode != PackageManager.MOVE_SUCCEEDED) { + // Clean up failed installation + if (mp.targetArgs != null) { + mp.targetArgs.doPostInstall(PackageManager.INSTALL_FAILED_INTERNAL_ERROR); + } + } else { + // Force a gc to clear things up. + Runtime.getRuntime().gc(); + // Delete older code + synchronized (mInstallLock) { + mp.srcArgs.doPostDeleteLI(true); + } + } - // Allow more operations on this file if we didn't fail because - // an operation was already pending for this package. - if (returnCode != PackageManager.MOVE_FAILED_OPERATION_PENDING) { - synchronized (mPackages) { - PackageParser.Package pkg = mPackages.get(mp.packageName); - if (pkg != null) { - pkg.mOperationPending = false; + // Allow more operations on this file if we didn't fail because + // an operation was already pending for this package. + if (returnCode != PackageManager.MOVE_FAILED_OPERATION_PENDING) { + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(mp.packageName); + if (pkg != null) { + pkg.mOperationPending = false; } } - } + } - IPackageMoveObserver observer = mp.observer; - if (observer != null) { - try { - observer.packageMoved(mp.packageName, returnCode); - } catch (RemoteException e) { - Log.i(TAG, "Observer no longer exists."); - } - } - } - }); - } + IPackageMoveObserver observer = mp.observer; + if (observer != null) { + try { + observer.packageMoved(mp.packageName, returnCode); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + } + } + }); + } - public boolean setInstallLocation(int loc) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS, null); - if (getInstallLocation() == loc) { - return true; - } - if (loc == PackageHelper.APP_INSTALL_AUTO || - loc == PackageHelper.APP_INSTALL_INTERNAL || - loc == PackageHelper.APP_INSTALL_EXTERNAL) { - android.provider.Settings.System.putInt(mContext.getContentResolver(), - android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, loc); - return true; - } - return false; + public boolean setInstallLocation(int loc) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS, + null); + if (getInstallLocation() == loc) { + return true; + } + if (loc == PackageHelper.APP_INSTALL_AUTO || loc == PackageHelper.APP_INSTALL_INTERNAL + || loc == PackageHelper.APP_INSTALL_EXTERNAL) { + android.provider.Settings.System.putInt(mContext.getContentResolver(), + android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, loc); + return true; + } + return false; } - public int getInstallLocation() { - return android.provider.Settings.System.getInt(mContext.getContentResolver(), - android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, PackageHelper.APP_INSTALL_AUTO); - } + public int getInstallLocation() { + return android.provider.Settings.System.getInt(mContext.getContentResolver(), + android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, + PackageHelper.APP_INSTALL_AUTO); + } } diff --git a/services/java/com/android/server/pm/PackageSetting.java b/services/java/com/android/server/pm/PackageSetting.java new file mode 100644 index 000000000000..efdc2b3bdfe1 --- /dev/null +++ b/services/java/com/android/server/pm/PackageSetting.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.content.pm.PackageParser; + +import java.io.File; + +/** + * Settings data for a particular package we know about. + */ +final class PackageSetting extends PackageSettingBase { + int userId; + PackageParser.Package pkg; + SharedUserSetting sharedUser; + + PackageSetting(String name, String realName, File codePath, File resourcePath, + String nativeLibraryPathString, int pVersionCode, int pkgFlags) { + super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode, + pkgFlags); + } + + /** + * New instance of PackageSetting replicating the original settings. + * Note that it keeps the same PackageParser.Package instance. + */ + PackageSetting(PackageSetting orig) { + super(orig); + + userId = orig.userId; + pkg = orig.pkg; + sharedUser = orig.sharedUser; + } + + @Override + public String toString() { + return "PackageSetting{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + name + "/" + userId + "}"; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java new file mode 100644 index 000000000000..e2f83adda16b --- /dev/null +++ b/services/java/com/android/server/pm/PackageSettingBase.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + + +import java.io.File; +import java.util.HashSet; + +/** + * Settings base class for pending and resolved classes. + */ +class PackageSettingBase extends GrantedPermissions { + /** + * Indicates the state of installation. Used by PackageManager to figure out + * incomplete installations. Say a package is being installed (the state is + * set to PKG_INSTALL_INCOMPLETE) and remains so till the package + * installation is successful or unsuccessful in which case the + * PackageManager will no longer maintain state information associated with + * the package. If some exception(like device freeze or battery being pulled + * out) occurs during installation of a package, the PackageManager needs + * this information to clean up the previously failed installation. + */ + static final int PKG_INSTALL_COMPLETE = 1; + static final int PKG_INSTALL_INCOMPLETE = 0; + + final String name; + final String realName; + File codePath; + String codePathString; + File resourcePath; + String resourcePathString; + String nativeLibraryPathString; + long timeStamp; + long firstInstallTime; + long lastUpdateTime; + int versionCode; + + boolean uidError; + + PackageSignatures signatures = new PackageSignatures(); + + boolean permissionsFixed; + boolean haveGids; + + // Whether this package is currently stopped, thus can not be + // started until explicitly launched by the user. + public boolean stopped; + + // Set to true if we have never launched this app. + public boolean notLaunched; + + /* Explicitly disabled components */ + HashSet<String> disabledComponents = new HashSet<String>(0); + /* Explicitly enabled components */ + HashSet<String> enabledComponents = new HashSet<String>(0); + int enabled = COMPONENT_ENABLED_STATE_DEFAULT; + int installStatus = PKG_INSTALL_COMPLETE; + + PackageSettingBase origPackage; + + /* package name of the app that installed this package */ + String installerPackageName; + PackageSettingBase(String name, String realName, File codePath, File resourcePath, + String nativeLibraryPathString, int pVersionCode, int pkgFlags) { + super(pkgFlags); + this.name = name; + this.realName = realName; + init(codePath, resourcePath, nativeLibraryPathString, pVersionCode); + } + + /** + * New instance of PackageSetting with one-level-deep cloning. + */ + @SuppressWarnings("unchecked") + PackageSettingBase(PackageSettingBase base) { + super(base); + + name = base.name; + realName = base.realName; + codePath = base.codePath; + codePathString = base.codePathString; + resourcePath = base.resourcePath; + resourcePathString = base.resourcePathString; + nativeLibraryPathString = base.nativeLibraryPathString; + timeStamp = base.timeStamp; + firstInstallTime = base.firstInstallTime; + lastUpdateTime = base.lastUpdateTime; + versionCode = base.versionCode; + + uidError = base.uidError; + + signatures = new PackageSignatures(base.signatures); + + permissionsFixed = base.permissionsFixed; + haveGids = base.haveGids; + stopped = base.stopped; + notLaunched = base.notLaunched; + + disabledComponents = (HashSet<String>) base.disabledComponents.clone(); + + enabledComponents = (HashSet<String>) base.enabledComponents.clone(); + + enabled = base.enabled; + installStatus = base.installStatus; + + origPackage = base.origPackage; + + installerPackageName = base.installerPackageName; + } + + void init(File codePath, File resourcePath, String nativeLibraryPathString, + int pVersionCode) { + this.codePath = codePath; + this.codePathString = codePath.toString(); + this.resourcePath = resourcePath; + this.resourcePathString = resourcePath.toString(); + this.nativeLibraryPathString = nativeLibraryPathString; + this.versionCode = pVersionCode; + } + + public void setInstallerPackageName(String packageName) { + installerPackageName = packageName; + } + + String getInstallerPackageName() { + return installerPackageName; + } + + public void setInstallStatus(int newStatus) { + installStatus = newStatus; + } + + public int getInstallStatus() { + return installStatus; + } + + public void setTimeStamp(long newStamp) { + timeStamp = newStamp; + } + + /** + * Make a shallow copy of this package settings. + */ + public void copyFrom(PackageSettingBase base) { + grantedPermissions = base.grantedPermissions; + gids = base.gids; + + timeStamp = base.timeStamp; + firstInstallTime = base.firstInstallTime; + lastUpdateTime = base.lastUpdateTime; + signatures = base.signatures; + permissionsFixed = base.permissionsFixed; + haveGids = base.haveGids; + stopped = base.stopped; + notLaunched = base.notLaunched; + disabledComponents = base.disabledComponents; + enabledComponents = base.enabledComponents; + enabled = base.enabled; + installStatus = base.installStatus; + } + + boolean enableComponentLPw(String componentClassName) { + boolean changed = disabledComponents.remove(componentClassName); + changed |= enabledComponents.add(componentClassName); + return changed; + } + + boolean disableComponentLPw(String componentClassName) { + boolean changed = enabledComponents.remove(componentClassName); + changed |= disabledComponents.add(componentClassName); + return changed; + } + + boolean restoreComponentLPw(String componentClassName) { + boolean changed = enabledComponents.remove(componentClassName); + changed |= disabledComponents.remove(componentClassName); + return changed; + } + + int getCurrentEnabledStateLPr(String componentName) { + if (enabledComponents.contains(componentName)) { + return COMPONENT_ENABLED_STATE_ENABLED; + } else if (disabledComponents.contains(componentName)) { + return COMPONENT_ENABLED_STATE_DISABLED; + } else { + return COMPONENT_ENABLED_STATE_DEFAULT; + } + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/pm/PackageSignatures.java b/services/java/com/android/server/pm/PackageSignatures.java new file mode 100644 index 000000000000..a25ec6cee508 --- /dev/null +++ b/services/java/com/android/server/pm/PackageSignatures.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import android.content.pm.Signature; +import android.util.Log; + +import java.io.IOException; +import java.util.ArrayList; + +class PackageSignatures { + Signature[] mSignatures; + + PackageSignatures(PackageSignatures orig) { + if (orig != null && orig.mSignatures != null) { + mSignatures = orig.mSignatures.clone(); + } + } + + PackageSignatures(Signature[] sigs) { + assignSignatures(sigs); + } + + PackageSignatures() { + } + + void writeXml(XmlSerializer serializer, String tagName, + ArrayList<Signature> pastSignatures) throws IOException { + if (mSignatures == null) { + return; + } + serializer.startTag(null, tagName); + serializer.attribute(null, "count", + Integer.toString(mSignatures.length)); + for (int i=0; i<mSignatures.length; i++) { + serializer.startTag(null, "cert"); + final Signature sig = mSignatures[i]; + final int sigHash = sig.hashCode(); + final int numPast = pastSignatures.size(); + int j; + for (j=0; j<numPast; j++) { + Signature pastSig = pastSignatures.get(j); + if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) { + serializer.attribute(null, "index", Integer.toString(j)); + break; + } + } + if (j >= numPast) { + pastSignatures.add(sig); + serializer.attribute(null, "index", Integer.toString(numPast)); + serializer.attribute(null, "key", sig.toCharsString()); + } + serializer.endTag(null, "cert"); + } + serializer.endTag(null, tagName); + } + + void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures) + throws IOException, XmlPullParserException { + String countStr = parser.getAttributeValue(null, "count"); + if (countStr == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <signatures> has" + + " no count at " + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + } + final int count = Integer.parseInt(countStr); + mSignatures = new Signature[count]; + int pos = 0; + + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("cert")) { + if (pos < count) { + String index = parser.getAttributeValue(null, "index"); + if (index != null) { + try { + int idx = Integer.parseInt(index); + String key = parser.getAttributeValue(null, "key"); + if (key == null) { + if (idx >= 0 && idx < pastSignatures.size()) { + Signature sig = pastSignatures.get(idx); + if (sig != null) { + mSignatures[pos] = pastSignatures.get(idx); + pos++; + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <cert> " + + "index " + index + " is not defined at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <cert> " + + "index " + index + " is out of bounds at " + + parser.getPositionDescription()); + } + } else { + while (pastSignatures.size() <= idx) { + pastSignatures.add(null); + } + Signature sig = new Signature(key); + pastSignatures.set(idx, sig); + mSignatures[pos] = sig; + pos++; + } + } catch (NumberFormatException e) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <cert> " + + "index " + index + " is not a number at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <cert> has" + + " no index at " + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: too " + + "many <cert> tags, expected " + count + + " at " + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <cert>: " + + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + + if (pos < count) { + // Should never happen -- there is an error in the written + // settings -- but if it does we don't want to generate + // a bad array. + Signature[] newSigs = new Signature[pos]; + System.arraycopy(mSignatures, 0, newSigs, 0, pos); + mSignatures = newSigs; + } + } + + void assignSignatures(Signature[] sigs) { + if (sigs == null) { + mSignatures = null; + return; + } + mSignatures = new Signature[sigs.length]; + for (int i=0; i<sigs.length; i++) { + mSignatures[i] = sigs[i]; + } + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(128); + buf.append("PackageSignatures{"); + buf.append(Integer.toHexString(System.identityHashCode(this))); + buf.append(" ["); + if (mSignatures != null) { + for (int i=0; i<mSignatures.length; i++) { + if (i > 0) buf.append(", "); + buf.append(Integer.toHexString( + System.identityHashCode(mSignatures[i]))); + } + } + buf.append("]}"); + return buf.toString(); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/pm/PendingPackage.java b/services/java/com/android/server/pm/PendingPackage.java new file mode 100644 index 000000000000..c17cc46dbfb5 --- /dev/null +++ b/services/java/com/android/server/pm/PendingPackage.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import java.io.File; + +final class PendingPackage extends PackageSettingBase { + final int sharedId; + + PendingPackage(String name, String realName, File codePath, File resourcePath, + String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) { + super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode, + pkgFlags); + this.sharedId = sharedId; + } +} diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java new file mode 100644 index 000000000000..b100eb1621ef --- /dev/null +++ b/services/java/com/android/server/pm/PreferredActivity.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import com.android.internal.util.XmlUtils; +import com.android.server.PreferredComponent; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import android.content.ComponentName; +import android.content.IntentFilter; +import android.util.Log; + +import java.io.IOException; + +class PreferredActivity extends IntentFilter implements PreferredComponent.Callbacks { + private static final String TAG = "PreferredActivity"; + + private static final boolean DEBUG_FILTERS = false; + + final PreferredComponent mPref; + + PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { + super(filter); + mPref = new PreferredComponent(this, match, set, activity); + } + + PreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException { + mPref = new PreferredComponent(this, parser); + } + + public void writeToXml(XmlSerializer serializer) throws IOException { + mPref.writeToXml(serializer); + serializer.startTag(null, "filter"); + super.writeToXml(serializer); + serializer.endTag(null, "filter"); + } + + public boolean onReadTag(String tagName, XmlPullParser parser) throws XmlPullParserException, + IOException { + if (tagName.equals("filter")) { + if (DEBUG_FILTERS) { + Log.i(TAG, "Starting to parse filter..."); + } + readFromXml(parser); + if (DEBUG_FILTERS) { + Log.i(TAG, "Finished filter: depth=" + parser.getDepth() + " tag=" + + parser.getName()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <preferred-activities>: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + return true; + } +} diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java new file mode 100644 index 000000000000..11dde752272c --- /dev/null +++ b/services/java/com/android/server/pm/Settings.java @@ -0,0 +1,2224 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.JournaledFile; +import com.android.internal.util.XmlUtils; +import com.android.server.IntentResolver; +import com.android.server.pm.PackageManagerService.DumpState; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.ComponentInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PermissionInfo; +import android.content.pm.Signature; +import android.os.Binder; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Process; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.Xml; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +/** + * Holds information about dynamic settings. + */ +final class Settings { + private static final String TAG = "PackageSettings"; + + private static final boolean DEBUG_STOPPED = false; + + private final File mSettingsFilename; + private final File mBackupSettingsFilename; + private final File mPackageListFilename; + private final File mStoppedPackagesFilename; + private final File mBackupStoppedPackagesFilename; + final HashMap<String, PackageSetting> mPackages = + new HashMap<String, PackageSetting>(); + // List of replaced system applications + final HashMap<String, PackageSetting> mDisabledSysPackages = + new HashMap<String, PackageSetting>(); + + // These are the last platform API version we were using for + // the apps installed on internal and external storage. It is + // used to grant newer permissions one time during a system upgrade. + int mInternalSdkPlatform; + int mExternalSdkPlatform; + + // The user's preferred activities associated with particular intent + // filters. + final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities = + new IntentResolver<PreferredActivity, PreferredActivity>() { + @Override + protected String packageForFilter(PreferredActivity filter) { + return filter.mPref.mComponent.getPackageName(); + } + @Override + protected void dumpFilter(PrintWriter out, String prefix, + PreferredActivity filter) { + filter.mPref.dump(out, prefix, filter); + } + }; + final HashMap<String, SharedUserSetting> mSharedUsers = + new HashMap<String, SharedUserSetting>(); + private final ArrayList<Object> mUserIds = new ArrayList<Object>(); + private final SparseArray<Object> mOtherUserIds = + new SparseArray<Object>(); + + // For reading/writing settings file. + private final ArrayList<Signature> mPastSignatures = + new ArrayList<Signature>(); + + // Mapping from permission names to info about them. + final HashMap<String, BasePermission> mPermissions = + new HashMap<String, BasePermission>(); + + // Mapping from permission tree names to info about them. + final HashMap<String, BasePermission> mPermissionTrees = + new HashMap<String, BasePermission>(); + + // Packages that have been uninstalled and still need their external + // storage data deleted. + final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>(); + + // Packages that have been renamed since they were first installed. + // Keys are the new names of the packages, values are the original + // names. The packages appear everwhere else under their original + // names. + final HashMap<String, String> mRenamedPackages = new HashMap<String, String>(); + + final StringBuilder mReadMessages = new StringBuilder(); + + /** + * Used to track packages that have a shared user ID that hasn't been read + * in yet. + * <p> + * TODO: make this just a local variable that is passed in during package + * scanning to make it less confusing. + */ + private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>(); + + Settings() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + // TODO(oam): This secure dir creation needs to be moved somewhere else (later) + File systemSecureDir = new File(dataDir, "secure/system"); + systemDir.mkdirs(); + systemSecureDir.mkdirs(); + FileUtils.setPermissions(systemDir.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG + |FileUtils.S_IROTH|FileUtils.S_IXOTH, + -1, -1); + FileUtils.setPermissions(systemSecureDir.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG + |FileUtils.S_IROTH|FileUtils.S_IXOTH, + -1, -1); + mSettingsFilename = new File(systemDir, "packages.xml"); + mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); + mPackageListFilename = new File(systemDir, "packages.list"); + mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml"); + mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml"); + } + + PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage, + String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, + String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) { + final String name = pkg.packageName; + PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath, + resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add); + return p; + } + + PackageSetting peekPackageLPr(String name) { + return mPackages.get(name); + } + + void setInstallStatus(String pkgName, int status) { + PackageSetting p = mPackages.get(pkgName); + if(p != null) { + if(p.getInstallStatus() != status) { + p.setInstallStatus(status); + } + } + } + + void setInstallerPackageName(String pkgName, + String installerPkgName) { + PackageSetting p = mPackages.get(pkgName); + if(p != null) { + p.setInstallerPackageName(installerPkgName); + } + } + + SharedUserSetting getSharedUserLPw(String name, + int pkgFlags, boolean create) { + SharedUserSetting s = mSharedUsers.get(name); + if (s == null) { + if (!create) { + return null; + } + s = new SharedUserSetting(name, pkgFlags); + if (PackageManagerService.MULTIPLE_APPLICATION_UIDS) { + s.userId = newUserIdLPw(s); + } else { + s.userId = PackageManagerService.FIRST_APPLICATION_UID; + } + Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId); + // < 0 means we couldn't assign a userid; fall out and return + // s, which is currently null + if (s.userId >= 0) { + mSharedUsers.put(name, s); + } + } + + return s; + } + + boolean disableSystemPackageLPw(String name) { + final PackageSetting p = mPackages.get(name); + if(p == null) { + Log.w(PackageManagerService.TAG, "Package:"+name+" is not an installed package"); + return false; + } + final PackageSetting dp = mDisabledSysPackages.get(name); + // always make sure the system package code and resource paths dont change + if (dp == null) { + if((p.pkg != null) && (p.pkg.applicationInfo != null)) { + p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } + mDisabledSysPackages.put(name, p); + + // a little trick... when we install the new package, we don't + // want to modify the existing PackageSetting for the built-in + // version. so at this point we need a new PackageSetting that + // is okay to muck with. + PackageSetting newp = new PackageSetting(p); + replacePackageLPw(name, newp); + return true; + } + return false; + } + + PackageSetting enableSystemPackageLPw(String name) { + PackageSetting p = mDisabledSysPackages.get(name); + if(p == null) { + Log.w(PackageManagerService.TAG, "Package:"+name+" is not disabled"); + return null; + } + // Reset flag in ApplicationInfo object + if((p.pkg != null) && (p.pkg.applicationInfo != null)) { + p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } + PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath, + p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags); + mDisabledSysPackages.remove(name); + return ret; + } + + PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, + String nativeLibraryPathString, int uid, int vc, int pkgFlags) { + PackageSetting p = mPackages.get(name); + if (p != null) { + if (p.userId == uid) { + return p; + } + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Adding duplicate package, keeping first: " + name); + return null; + } + p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, + vc, pkgFlags); + p.userId = uid; + if (addUserIdLPw(uid, p, name)) { + mPackages.put(name, p); + return p; + } + return null; + } + + SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) { + SharedUserSetting s = mSharedUsers.get(name); + if (s != null) { + if (s.userId == uid) { + return s; + } + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Adding duplicate shared user, keeping first: " + name); + return null; + } + s = new SharedUserSetting(name, pkgFlags); + s.userId = uid; + if (addUserIdLPw(uid, s, name)) { + mSharedUsers.put(name, s); + return s; + } + return null; + } + + // Transfer ownership of permissions from one package to another. + void transferPermissionsLPw(String origPkg, String newPkg) { + // Transfer ownership of permissions to the new package. + for (int i=0; i<2; i++) { + HashMap<String, BasePermission> permissions = + i == 0 ? mPermissionTrees : mPermissions; + for (BasePermission bp : permissions.values()) { + if (origPkg.equals(bp.sourcePackage)) { + if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, + "Moving permission " + bp.name + + " from pkg " + bp.sourcePackage + + " to " + newPkg); + bp.sourcePackage = newPkg; + bp.packageSetting = null; + bp.perm = null; + if (bp.pendingInfo != null) { + bp.pendingInfo.packageName = newPkg; + } + bp.uid = 0; + bp.gids = null; + } + } + } + } + + private PackageSetting getPackageLPw(String name, PackageSetting origPackage, + String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, + String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) { + PackageSetting p = mPackages.get(name); + if (p != null) { + if (!p.codePath.equals(codePath)) { + // Check to see if its a disabled system app + if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { + // This is an updated system app with versions in both system + // and data partition. Just let the most recent version + // take precedence. + Slog.w(PackageManagerService.TAG, "Trying to update system app code path from " + + p.codePathString + " to " + codePath.toString()); + } else { + // Just a change in the code path is not an issue, but + // let's log a message about it. + Slog.i(PackageManagerService.TAG, "Package " + name + " codePath changed from " + p.codePath + + " to " + codePath + "; Retaining data and using new"); + /* + * Since we've changed paths, we need to prefer the new + * native library path over the one stored in the + * package settings since we might have moved from + * internal to external storage or vice versa. + */ + p.nativeLibraryPathString = nativeLibraryPathString; + } + } + if (p.sharedUser != sharedUser) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Package " + name + " shared user changed from " + + (p.sharedUser != null ? p.sharedUser.name : "<nothing>") + + " to " + + (sharedUser != null ? sharedUser.name : "<nothing>") + + "; replacing with new"); + p = null; + } else { + if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) { + // If what we are scanning is a system package, then + // make it so, regardless of whether it was previously + // installed only in the data partition. + p.pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } + } + } + if (p == null) { + // Create a new PackageSettings entry. this can end up here because + // of code path mismatch or user id mismatch of an updated system partition + if (!create) { + return null; + } + if (origPackage != null) { + // We are consuming the data from an existing package. + p = new PackageSetting(origPackage.name, name, codePath, resourcePath, + nativeLibraryPathString, vc, pkgFlags); + if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + name + + " is adopting original package " + origPackage.name); + // Note that we will retain the new package's signature so + // that we can keep its data. + PackageSignatures s = p.signatures; + p.copyFrom(origPackage); + p.signatures = s; + p.sharedUser = origPackage.sharedUser; + p.userId = origPackage.userId; + p.origPackage = origPackage; + mRenamedPackages.put(name, origPackage.name); + name = origPackage.name; + // Update new package state. + p.setTimeStamp(codePath.lastModified()); + } else { + p = new PackageSetting(name, realName, codePath, resourcePath, + nativeLibraryPathString, vc, pkgFlags); + p.setTimeStamp(codePath.lastModified()); + p.sharedUser = sharedUser; + // If this is not a system app, it starts out stopped. + if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { + if (DEBUG_STOPPED) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(PackageManagerService.TAG, "Stopping package " + name, e); + } + p.stopped = true; + p.notLaunched = true; + } + if (sharedUser != null) { + p.userId = sharedUser.userId; + } else if (PackageManagerService.MULTIPLE_APPLICATION_UIDS) { + // Clone the setting here for disabled system packages + PackageSetting dis = mDisabledSysPackages.get(name); + if (dis != null) { + // For disabled packages a new setting is created + // from the existing user id. This still has to be + // added to list of user id's + // Copy signatures from previous setting + if (dis.signatures.mSignatures != null) { + p.signatures.mSignatures = dis.signatures.mSignatures.clone(); + } + p.userId = dis.userId; + // Clone permissions + p.grantedPermissions = new HashSet<String>(dis.grantedPermissions); + // Clone component info + p.disabledComponents = new HashSet<String>(dis.disabledComponents); + p.enabledComponents = new HashSet<String>(dis.enabledComponents); + // Add new setting to list of user ids + addUserIdLPw(p.userId, p, name); + } else { + // Assign new user id + p.userId = newUserIdLPw(p); + } + } else { + p.userId = PackageManagerService.FIRST_APPLICATION_UID; + } + } + if (p.userId < 0) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Package " + name + " could not be assigned a valid uid"); + return null; + } + if (add) { + // Finish adding new package by adding it and updating shared + // user preferences + addPackageSettingLPw(p, name, sharedUser); + } + } + return p; + } + + void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) { + p.pkg = pkg; + pkg.mSetEnabled = p.enabled; + pkg.mSetStopped = p.stopped; + final String codePath = pkg.applicationInfo.sourceDir; + final String resourcePath = pkg.applicationInfo.publicSourceDir; + // Update code path if needed + if (!codePath.equalsIgnoreCase(p.codePathString)) { + Slog.w(PackageManagerService.TAG, "Code path for pkg : " + p.pkg.packageName + + " changing from " + p.codePathString + " to " + codePath); + p.codePath = new File(codePath); + p.codePathString = codePath; + } + //Update resource path if needed + if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) { + Slog.w(PackageManagerService.TAG, "Resource path for pkg : " + p.pkg.packageName + + " changing from " + p.resourcePathString + " to " + resourcePath); + p.resourcePath = new File(resourcePath); + p.resourcePathString = resourcePath; + } + // Update the native library path if needed + final String nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir; + if (nativeLibraryPath != null + && !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) { + p.nativeLibraryPathString = nativeLibraryPath; + } + // Update version code if needed + if (pkg.mVersionCode != p.versionCode) { + p.versionCode = pkg.mVersionCode; + } + // Update signatures if needed. + if (p.signatures.mSignatures == null) { + p.signatures.assignSignatures(pkg.mSignatures); + } + // If this app defines a shared user id initialize + // the shared user signatures as well. + if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) { + p.sharedUser.signatures.assignSignatures(pkg.mSignatures); + } + addPackageSettingLPw(p, pkg.packageName, p.sharedUser); + } + + // Utility method that adds a PackageSetting to mPackages and + // completes updating the shared user attributes + private void addPackageSettingLPw(PackageSetting p, String name, + SharedUserSetting sharedUser) { + mPackages.put(name, p); + if (sharedUser != null) { + if (p.sharedUser != null && p.sharedUser != sharedUser) { + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Package " + p.name + " was user " + + p.sharedUser + " but is now " + sharedUser + + "; I am not changing its files so it will probably fail!"); + p.sharedUser.packages.remove(p); + } else if (p.userId != sharedUser.userId) { + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Package " + p.name + " was user id " + p.userId + + " but is now user " + sharedUser + + " with id " + sharedUser.userId + + "; I am not changing its files so it will probably fail!"); + } + + sharedUser.packages.add(p); + p.sharedUser = sharedUser; + p.userId = sharedUser.userId; + } + } + + /* + * Update the shared user setting when a package using + * specifying the shared user id is removed. The gids + * associated with each permission of the deleted package + * are removed from the shared user's gid list only if its + * not in use by other permissions of packages in the + * shared user setting. + */ + void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) { + if ((deletedPs == null) || (deletedPs.pkg == null)) { + Slog.i(PackageManagerService.TAG, + "Trying to update info for null package. Just ignoring"); + return; + } + // No sharedUserId + if (deletedPs.sharedUser == null) { + return; + } + SharedUserSetting sus = deletedPs.sharedUser; + // Update permissions + for (String eachPerm : deletedPs.pkg.requestedPermissions) { + boolean used = false; + if (!sus.grantedPermissions.contains(eachPerm)) { + continue; + } + for (PackageSetting pkg:sus.packages) { + if (pkg.pkg != null && + !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) && + pkg.pkg.requestedPermissions.contains(eachPerm)) { + used = true; + break; + } + } + if (!used) { + // can safely delete this permission from list + sus.grantedPermissions.remove(eachPerm); + } + } + // Update gids + int newGids[] = globalGids; + for (String eachPerm : sus.grantedPermissions) { + BasePermission bp = mPermissions.get(eachPerm); + if (bp != null) { + newGids = PackageManagerService.appendInts(newGids, bp.gids); + } + } + sus.gids = newGids; + } + + int removePackageLPw(String name) { + final PackageSetting p = mPackages.get(name); + if (p != null) { + mPackages.remove(name); + if (p.sharedUser != null) { + p.sharedUser.packages.remove(p); + if (p.sharedUser.packages.size() == 0) { + mSharedUsers.remove(p.sharedUser.name); + removeUserIdLPw(p.sharedUser.userId); + return p.sharedUser.userId; + } + } else { + removeUserIdLPw(p.userId); + return p.userId; + } + } + return -1; + } + + private void replacePackageLPw(String name, PackageSetting newp) { + final PackageSetting p = mPackages.get(name); + if (p != null) { + if (p.sharedUser != null) { + p.sharedUser.packages.remove(p); + p.sharedUser.packages.add(newp); + } else { + replaceUserIdLPw(p.userId, newp); + } + } + mPackages.put(name, newp); + } + + private boolean addUserIdLPw(int uid, Object obj, Object name) { + if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) { + return false; + } + + if (uid >= PackageManagerService.FIRST_APPLICATION_UID) { + int N = mUserIds.size(); + final int index = uid - PackageManagerService.FIRST_APPLICATION_UID; + while (index >= N) { + mUserIds.add(null); + N++; + } + if (mUserIds.get(index) != null) { + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Adding duplicate user id: " + uid + + " name=" + name); + return false; + } + mUserIds.set(index, obj); + } else { + if (mOtherUserIds.get(uid) != null) { + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Adding duplicate shared id: " + uid + + " name=" + name); + return false; + } + mOtherUserIds.put(uid, obj); + } + return true; + } + + public Object getUserIdLPr(int uid) { + if (uid >= PackageManagerService.FIRST_APPLICATION_UID) { + final int N = mUserIds.size(); + final int index = uid - PackageManagerService.FIRST_APPLICATION_UID; + return index < N ? mUserIds.get(index) : null; + } else { + return mOtherUserIds.get(uid); + } + } + + private void removeUserIdLPw(int uid) { + if (uid >= PackageManagerService.FIRST_APPLICATION_UID) { + final int N = mUserIds.size(); + final int index = uid - PackageManagerService.FIRST_APPLICATION_UID; + if (index < N) mUserIds.set(index, null); + } else { + mOtherUserIds.remove(uid); + } + } + + private void replaceUserIdLPw(int uid, Object obj) { + if (uid >= PackageManagerService.FIRST_APPLICATION_UID) { + final int N = mUserIds.size(); + final int index = uid - PackageManagerService.FIRST_APPLICATION_UID; + if (index < N) mUserIds.set(index, obj); + } else { + mOtherUserIds.put(uid, obj); + } + } + + void writeStoppedLPr() { + // Keep the old stopped packages around until we know the new ones have + // been successfully written. + if (mStoppedPackagesFilename.exists()) { + // Presence of backup settings file indicates that we failed + // to persist packages earlier. So preserve the older + // backup for future reference since the current packages + // might have been corrupted. + if (!mBackupStoppedPackagesFilename.exists()) { + if (!mStoppedPackagesFilename.renameTo(mBackupStoppedPackagesFilename)) { + Log.wtf(PackageManagerService.TAG, "Unable to backup package manager stopped packages, " + + "current changes will be lost at reboot"); + return; + } + } else { + mStoppedPackagesFilename.delete(); + Slog.w(PackageManagerService.TAG, "Preserving older stopped packages backup"); + } + } + + try { + final FileOutputStream fstr = new FileOutputStream(mStoppedPackagesFilename); + final BufferedOutputStream str = new BufferedOutputStream(fstr); + + //XmlSerializer serializer = XmlUtils.serializerInstance(); + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(str, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, "stopped-packages"); + + for (final PackageSetting pkg : mPackages.values()) { + if (pkg.stopped) { + serializer.startTag(null, "pkg"); + serializer.attribute(null, "name", pkg.name); + if (pkg.notLaunched) { + serializer.attribute(null, "nl", "1"); + } + serializer.endTag(null, "pkg"); + } + } + + serializer.endTag(null, "stopped-packages"); + + serializer.endDocument(); + + str.flush(); + FileUtils.sync(fstr); + str.close(); + + // New settings successfully written, old ones are no longer + // needed. + mBackupStoppedPackagesFilename.delete(); + FileUtils.setPermissions(mStoppedPackagesFilename.toString(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR + |FileUtils.S_IRGRP|FileUtils.S_IWGRP + |FileUtils.S_IROTH, + -1, -1); + + // Done, all is good! + return; + } catch(java.io.IOException e) { + Log.wtf(PackageManagerService.TAG, "Unable to write package manager stopped packages, " + + " current changes will be lost at reboot", e); + } + + // Clean up partially written files + if (mStoppedPackagesFilename.exists()) { + if (!mStoppedPackagesFilename.delete()) { + Log.i(PackageManagerService.TAG, "Failed to clean up mangled file: " + mStoppedPackagesFilename); + } + } + } + + // Note: assumed "stopped" field is already cleared in all packages. + void readStoppedLPw() { + FileInputStream str = null; + if (mBackupStoppedPackagesFilename.exists()) { + try { + str = new FileInputStream(mBackupStoppedPackagesFilename); + mReadMessages.append("Reading from backup stopped packages file\n"); + PackageManagerService.reportSettingsProblem(Log.INFO, "Need to read from backup stopped packages file"); + if (mSettingsFilename.exists()) { + // If both the backup and normal file exist, we + // ignore the normal one since it might have been + // corrupted. + Slog.w(PackageManagerService.TAG, "Cleaning up stopped packages file " + + mStoppedPackagesFilename); + mStoppedPackagesFilename.delete(); + } + } catch (java.io.IOException e) { + // We'll try for the normal settings file. + } + } + + try { + if (str == null) { + if (!mStoppedPackagesFilename.exists()) { + mReadMessages.append("No stopped packages file found\n"); + PackageManagerService.reportSettingsProblem(Log.INFO, "No stopped packages file file; " + + "assuming all started"); + // At first boot, make sure no packages are stopped. + // We usually want to have third party apps initialize + // in the stopped state, but not at first boot. + for (PackageSetting pkg : mPackages.values()) { + pkg.stopped = false; + pkg.notLaunched = false; + } + return; + } + str = new FileInputStream(mStoppedPackagesFilename); + } + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(str, null); + + int type; + while ((type=parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + mReadMessages.append("No start tag found in stopped packages file\n"); + PackageManagerService.reportSettingsProblem(Log.WARN, + "No start tag found in package manager stopped packages"); + return; + } + + int outerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("pkg")) { + String name = parser.getAttributeValue(null, "name"); + PackageSetting ps = mPackages.get(name); + if (ps != null) { + ps.stopped = true; + if ("1".equals(parser.getAttributeValue(null, "nl"))) { + ps.notLaunched = true; + } + } else { + Slog.w(PackageManagerService.TAG, "No package known for stopped package: " + name); + } + XmlUtils.skipCurrentTag(parser); + } else { + Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + str.close(); + + } catch(XmlPullParserException e) { + mReadMessages.append("Error reading: " + e.toString()); + PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading stopped packages: " + e); + Log.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages", e); + + } catch(java.io.IOException e) { + mReadMessages.append("Error reading: " + e.toString()); + PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); + Log.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages", e); + + } + } + + void writeLPr() { + //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); + + // Keep the old settings around until we know the new ones have + // been successfully written. + if (mSettingsFilename.exists()) { + // Presence of backup settings file indicates that we failed + // to persist settings earlier. So preserve the older + // backup for future reference since the current settings + // might have been corrupted. + if (!mBackupSettingsFilename.exists()) { + if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) { + Log.wtf(PackageManagerService.TAG, "Unable to backup package manager settings, " + + " current changes will be lost at reboot"); + return; + } + } else { + mSettingsFilename.delete(); + Slog.w(PackageManagerService.TAG, "Preserving older settings backup"); + } + } + + mPastSignatures.clear(); + + try { + FileOutputStream fstr = new FileOutputStream(mSettingsFilename); + BufferedOutputStream str = new BufferedOutputStream(fstr); + + //XmlSerializer serializer = XmlUtils.serializerInstance(); + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(str, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, "packages"); + + serializer.startTag(null, "last-platform-version"); + serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform)); + serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform)); + serializer.endTag(null, "last-platform-version"); + + serializer.startTag(null, "permission-trees"); + for (BasePermission bp : mPermissionTrees.values()) { + writePermissionLPr(serializer, bp); + } + serializer.endTag(null, "permission-trees"); + + serializer.startTag(null, "permissions"); + for (BasePermission bp : mPermissions.values()) { + writePermissionLPr(serializer, bp); + } + serializer.endTag(null, "permissions"); + + for (final PackageSetting pkg : mPackages.values()) { + writePackageLPr(serializer, pkg); + } + + for (final PackageSetting pkg : mDisabledSysPackages.values()) { + writeDisabledSysPackageLPr(serializer, pkg); + } + + serializer.startTag(null, "preferred-activities"); + for (final PreferredActivity pa : mPreferredActivities.filterSet()) { + serializer.startTag(null, "item"); + pa.writeToXml(serializer); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "preferred-activities"); + + for (final SharedUserSetting usr : mSharedUsers.values()) { + serializer.startTag(null, "shared-user"); + serializer.attribute(null, "name", usr.name); + serializer.attribute(null, "userId", + Integer.toString(usr.userId)); + usr.signatures.writeXml(serializer, "sigs", mPastSignatures); + serializer.startTag(null, "perms"); + for (String name : usr.grantedPermissions) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "perms"); + serializer.endTag(null, "shared-user"); + } + + if (mPackagesToBeCleaned.size() > 0) { + for (int i=0; i<mPackagesToBeCleaned.size(); i++) { + serializer.startTag(null, "cleaning-package"); + serializer.attribute(null, "name", mPackagesToBeCleaned.get(i)); + serializer.endTag(null, "cleaning-package"); + } + } + + if (mRenamedPackages.size() > 0) { + for (HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) { + serializer.startTag(null, "renamed-package"); + serializer.attribute(null, "new", e.getKey()); + serializer.attribute(null, "old", e.getValue()); + serializer.endTag(null, "renamed-package"); + } + } + + serializer.endTag(null, "packages"); + + serializer.endDocument(); + + str.flush(); + FileUtils.sync(fstr); + str.close(); + + // New settings successfully written, old ones are no longer + // needed. + mBackupSettingsFilename.delete(); + FileUtils.setPermissions(mSettingsFilename.toString(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR + |FileUtils.S_IRGRP|FileUtils.S_IWGRP + |FileUtils.S_IROTH, + -1, -1); + + // Write package list file now, use a JournaledFile. + // + File tempFile = new File(mPackageListFilename.toString() + ".tmp"); + JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile); + + fstr = new FileOutputStream(journal.chooseForWrite()); + str = new BufferedOutputStream(fstr); + try { + StringBuilder sb = new StringBuilder(); + for (final PackageSetting pkg : mPackages.values()) { + ApplicationInfo ai = pkg.pkg.applicationInfo; + String dataPath = ai.dataDir; + boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + + // Avoid any application that has a space in its path + // or that is handled by the system. + if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID) + continue; + + // we store on each line the following information for now: + // + // pkgName - package name + // userId - application-specific user id + // debugFlag - 0 or 1 if the package is debuggable. + // dataPath - path to package's data path + // + // NOTE: We prefer not to expose all ApplicationInfo flags for now. + // + // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS + // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES: + // system/core/run-as/run-as.c + // + sb.setLength(0); + sb.append(ai.packageName); + sb.append(" "); + sb.append((int)ai.uid); + sb.append(isDebug ? " 1 " : " 0 "); + sb.append(dataPath); + sb.append("\n"); + str.write(sb.toString().getBytes()); + } + str.flush(); + FileUtils.sync(fstr); + str.close(); + journal.commit(); + } + catch (Exception e) { + journal.rollback(); + } + + FileUtils.setPermissions(mPackageListFilename.toString(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR + |FileUtils.S_IRGRP|FileUtils.S_IWGRP + |FileUtils.S_IROTH, + -1, -1); + + writeStoppedLPr(); + + return; + + } catch(XmlPullParserException e) { + Log.wtf(PackageManagerService.TAG, "Unable to write package manager settings, " + + "current changes will be lost at reboot", e); + } catch(java.io.IOException e) { + Log.wtf(PackageManagerService.TAG, "Unable to write package manager settings, " + + "current changes will be lost at reboot", e); + } + // Clean up partially written files + if (mSettingsFilename.exists()) { + if (!mSettingsFilename.delete()) { + Log.wtf(PackageManagerService.TAG, "Failed to clean up mangled file: " + mSettingsFilename); + } + } + //Debug.stopMethodTracing(); + } + + void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "updated-package"); + serializer.attribute(null, "name", pkg.name); + if (pkg.realName != null) { + serializer.attribute(null, "realName", pkg.realName); + } + serializer.attribute(null, "codePath", pkg.codePathString); + serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); + serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); + serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime)); + serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); + if (!pkg.resourcePathString.equals(pkg.codePathString)) { + serializer.attribute(null, "resourcePath", pkg.resourcePathString); + } + if (pkg.nativeLibraryPathString != null) { + serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); + } + if (pkg.sharedUser == null) { + serializer.attribute(null, "userId", Integer.toString(pkg.userId)); + } else { + serializer.attribute(null, "sharedUserId", Integer.toString(pkg.userId)); + } + serializer.startTag(null, "perms"); + if (pkg.sharedUser == null) { + // If this is a shared user, the permissions will + // be written there. We still need to write an + // empty permissions list so permissionsFixed will + // be set. + for (final String name : pkg.grantedPermissions) { + BasePermission bp = mPermissions.get(name); + if (bp != null) { + // We only need to write signature or system permissions but + // this wont + // match the semantics of grantedPermissions. So write all + // permissions. + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + } + } + serializer.endTag(null, "perms"); + serializer.endTag(null, "updated-package"); + } + + void writePackageLPr(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "package"); + serializer.attribute(null, "name", pkg.name); + if (pkg.realName != null) { + serializer.attribute(null, "realName", pkg.realName); + } + serializer.attribute(null, "codePath", pkg.codePathString); + if (!pkg.resourcePathString.equals(pkg.codePathString)) { + serializer.attribute(null, "resourcePath", pkg.resourcePathString); + } + if (pkg.nativeLibraryPathString != null) { + serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); + } + serializer.attribute(null, "flags", Integer.toString(pkg.pkgFlags)); + serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); + serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); + serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime)); + serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); + if (pkg.sharedUser == null) { + serializer.attribute(null, "userId", Integer.toString(pkg.userId)); + } else { + serializer.attribute(null, "sharedUserId", Integer.toString(pkg.userId)); + } + if (pkg.uidError) { + serializer.attribute(null, "uidError", "true"); + } + if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { + serializer.attribute(null, "enabled", + pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED ? "true" : "false"); + } + if (pkg.installStatus == PackageSettingBase.PKG_INSTALL_INCOMPLETE) { + serializer.attribute(null, "installStatus", "false"); + } + if (pkg.installerPackageName != null) { + serializer.attribute(null, "installer", pkg.installerPackageName); + } + pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); + if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { + serializer.startTag(null, "perms"); + if (pkg.sharedUser == null) { + // If this is a shared user, the permissions will + // be written there. We still need to write an + // empty permissions list so permissionsFixed will + // be set. + for (final String name : pkg.grantedPermissions) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + } + serializer.endTag(null, "perms"); + } + if (pkg.disabledComponents.size() > 0) { + serializer.startTag(null, "disabled-components"); + for (final String name : pkg.disabledComponents) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "disabled-components"); + } + if (pkg.enabledComponents.size() > 0) { + serializer.startTag(null, "enabled-components"); + for (final String name : pkg.enabledComponents) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "enabled-components"); + } + + serializer.endTag(null, "package"); + } + + void writePermissionLPr(XmlSerializer serializer, BasePermission bp) + throws XmlPullParserException, java.io.IOException { + if (bp.type != BasePermission.TYPE_BUILTIN && bp.sourcePackage != null) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", bp.name); + serializer.attribute(null, "package", bp.sourcePackage); + if (bp.protectionLevel != PermissionInfo.PROTECTION_NORMAL) { + serializer.attribute(null, "protection", Integer.toString(bp.protectionLevel)); + } + if (PackageManagerService.DEBUG_SETTINGS) + Log.v(PackageManagerService.TAG, "Writing perm: name=" + bp.name + " type=" + + bp.type); + if (bp.type == BasePermission.TYPE_DYNAMIC) { + final PermissionInfo pi = bp.perm != null ? bp.perm.info : bp.pendingInfo; + if (pi != null) { + serializer.attribute(null, "type", "dynamic"); + if (pi.icon != 0) { + serializer.attribute(null, "icon", Integer.toString(pi.icon)); + } + if (pi.nonLocalizedLabel != null) { + serializer.attribute(null, "label", pi.nonLocalizedLabel.toString()); + } + } + } + serializer.endTag(null, "item"); + } + } + + ArrayList<PackageSetting> getListOfIncompleteInstallPackagesLPr() { + final HashSet<String> kList = new HashSet<String>(mPackages.keySet()); + final Iterator<String> its = kList.iterator(); + final ArrayList<PackageSetting> ret = new ArrayList<PackageSetting>(); + while (its.hasNext()) { + final String key = its.next(); + final PackageSetting ps = mPackages.get(key); + if (ps.getInstallStatus() == PackageSettingBase.PKG_INSTALL_INCOMPLETE) { + ret.add(ps); + } + } + return ret; + } + + boolean readLPw() { + FileInputStream str = null; + if (mBackupSettingsFilename.exists()) { + try { + str = new FileInputStream(mBackupSettingsFilename); + mReadMessages.append("Reading from backup settings file\n"); + PackageManagerService.reportSettingsProblem(Log.INFO, + "Need to read from backup settings file"); + if (mSettingsFilename.exists()) { + // If both the backup and settings file exist, we + // ignore the settings since it might have been + // corrupted. + Slog.w(PackageManagerService.TAG, "Cleaning up settings file " + + mSettingsFilename); + mSettingsFilename.delete(); + } + } catch (java.io.IOException e) { + // We'll try for the normal settings file. + } + } + + mPendingPackages.clear(); + mPastSignatures.clear(); + + try { + if (str == null) { + if (!mSettingsFilename.exists()) { + mReadMessages.append("No settings file found\n"); + PackageManagerService.reportSettingsProblem(Log.INFO, + "No settings file; creating initial state"); + return false; + } + str = new FileInputStream(mSettingsFilename); + } + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(str, null); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + mReadMessages.append("No start tag found in settings file\n"); + PackageManagerService.reportSettingsProblem(Log.WARN, + "No start tag found in package manager settings"); + Log + .wtf(PackageManagerService.TAG, + "No start tag found in package manager settings"); + return false; + } + + int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("package")) { + readPackageLPw(parser); + } else if (tagName.equals("permissions")) { + readPermissionsLPw(mPermissions, parser); + } else if (tagName.equals("permission-trees")) { + readPermissionsLPw(mPermissionTrees, parser); + } else if (tagName.equals("shared-user")) { + readSharedUserLPw(parser); + } else if (tagName.equals("preferred-packages")) { + // no longer used. + } else if (tagName.equals("preferred-activities")) { + readPreferredActivitiesLPw(parser); + } else if (tagName.equals("updated-package")) { + readDisabledSysPackageLPw(parser); + } else if (tagName.equals("cleaning-package")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + mPackagesToBeCleaned.add(name); + } + } else if (tagName.equals("renamed-package")) { + String nname = parser.getAttributeValue(null, "new"); + String oname = parser.getAttributeValue(null, "old"); + if (nname != null && oname != null) { + mRenamedPackages.put(nname, oname); + } + } else if (tagName.equals("last-platform-version")) { + mInternalSdkPlatform = mExternalSdkPlatform = 0; + try { + String internal = parser.getAttributeValue(null, "internal"); + if (internal != null) { + mInternalSdkPlatform = Integer.parseInt(internal); + } + String external = parser.getAttributeValue(null, "external"); + if (external != null) { + mExternalSdkPlatform = Integer.parseInt(external); + } + } catch (NumberFormatException e) { + } + } else { + Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + str.close(); + + } catch (XmlPullParserException e) { + mReadMessages.append("Error reading: " + e.toString()); + PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); + Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e); + + } catch (java.io.IOException e) { + mReadMessages.append("Error reading: " + e.toString()); + PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); + Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e); + + } + + final int N = mPendingPackages.size(); + for (int i = 0; i < N; i++) { + final PendingPackage pp = mPendingPackages.get(i); + Object idObj = getUserIdLPr(pp.sharedId); + if (idObj != null && idObj instanceof SharedUserSetting) { + PackageSetting p = getPackageLPw(pp.name, null, pp.realName, + (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, + pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true); + if (p == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unable to create application package for " + pp.name); + continue; + } + p.copyFrom(pp); + } else if (idObj != null) { + String msg = "Bad package setting: package " + pp.name + " has shared uid " + + pp.sharedId + " that is not a shared uid\n"; + mReadMessages.append(msg); + PackageManagerService.reportSettingsProblem(Log.ERROR, msg); + } else { + String msg = "Bad package setting: package " + pp.name + " has shared uid " + + pp.sharedId + " that is not defined\n"; + mReadMessages.append(msg); + PackageManagerService.reportSettingsProblem(Log.ERROR, msg); + } + } + mPendingPackages.clear(); + + readStoppedLPw(); + + mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, " + + mSharedUsers.size() + " shared uids\n"); + + return true; + } + + private int readInt(XmlPullParser parser, String ns, String name, int defValue) { + String v = parser.getAttributeValue(ns, name); + try { + if (v == null) { + return defValue; + } + return Integer.parseInt(v); + } catch (NumberFormatException e) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: attribute " + name + + " has bad integer value " + v + " at " + + parser.getPositionDescription()); + } + return defValue; + } + + private void readPermissionsLPw(HashMap<String, BasePermission> out, XmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + final String tagName = parser.getName(); + if (tagName.equals("item")) { + final String name = parser.getAttributeValue(null, "name"); + final String sourcePackage = parser.getAttributeValue(null, "package"); + final String ptype = parser.getAttributeValue(null, "type"); + if (name != null && sourcePackage != null) { + final boolean dynamic = "dynamic".equals(ptype); + final BasePermission bp = new BasePermission(name, sourcePackage, + dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL); + bp.protectionLevel = readInt(parser, null, "protection", + PermissionInfo.PROTECTION_NORMAL); + if (dynamic) { + PermissionInfo pi = new PermissionInfo(); + pi.packageName = sourcePackage.intern(); + pi.name = name.intern(); + pi.icon = readInt(parser, null, "icon", 0); + pi.nonLocalizedLabel = parser.getAttributeValue(null, "label"); + pi.protectionLevel = bp.protectionLevel; + bp.pendingInfo = pi; + } + out.put(bp.name, bp); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: permissions has" + " no name at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element reading permissions: " + parser.getName() + " at " + + parser.getPositionDescription()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException, + IOException { + String name = parser.getAttributeValue(null, "name"); + String realName = parser.getAttributeValue(null, "realName"); + String codePathStr = parser.getAttributeValue(null, "codePath"); + String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); + String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); + if (resourcePathStr == null) { + resourcePathStr = codePathStr; + } + String version = parser.getAttributeValue(null, "version"); + int versionCode = 0; + if (version != null) { + try { + versionCode = Integer.parseInt(version); + } catch (NumberFormatException e) { + } + } + + int pkgFlags = 0; + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), + new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags); + String timeStampStr = parser.getAttributeValue(null, "ft"); + if (timeStampStr != null) { + try { + long timeStamp = Long.parseLong(timeStampStr, 16); + ps.setTimeStamp(timeStamp); + } catch (NumberFormatException e) { + } + } else { + timeStampStr = parser.getAttributeValue(null, "ts"); + if (timeStampStr != null) { + try { + long timeStamp = Long.parseLong(timeStampStr); + ps.setTimeStamp(timeStamp); + } catch (NumberFormatException e) { + } + } + } + timeStampStr = parser.getAttributeValue(null, "it"); + if (timeStampStr != null) { + try { + ps.firstInstallTime = Long.parseLong(timeStampStr, 16); + } catch (NumberFormatException e) { + } + } + timeStampStr = parser.getAttributeValue(null, "ut"); + if (timeStampStr != null) { + try { + ps.lastUpdateTime = Long.parseLong(timeStampStr, 16); + } catch (NumberFormatException e) { + } + } + String idStr = parser.getAttributeValue(null, "userId"); + ps.userId = idStr != null ? Integer.parseInt(idStr) : 0; + if (ps.userId <= 0) { + String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); + ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; + } + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("perms")) { + readGrantedPermissionsLPw(parser, ps.grantedPermissions); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <updated-package>: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + mDisabledSysPackages.put(name, ps); + } + + private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException { + String name = null; + String realName = null; + String idStr = null; + String sharedIdStr = null; + String codePathStr = null; + String resourcePathStr = null; + String nativeLibraryPathStr = null; + String systemStr = null; + String installerPackageName = null; + String uidError = null; + int pkgFlags = 0; + long timeStamp = 0; + long firstInstallTime = 0; + long lastUpdateTime = 0; + PackageSettingBase packageSetting = null; + String version = null; + int versionCode = 0; + try { + name = parser.getAttributeValue(null, "name"); + realName = parser.getAttributeValue(null, "realName"); + idStr = parser.getAttributeValue(null, "userId"); + uidError = parser.getAttributeValue(null, "uidError"); + sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); + codePathStr = parser.getAttributeValue(null, "codePath"); + resourcePathStr = parser.getAttributeValue(null, "resourcePath"); + nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); + version = parser.getAttributeValue(null, "version"); + if (version != null) { + try { + versionCode = Integer.parseInt(version); + } catch (NumberFormatException e) { + } + } + installerPackageName = parser.getAttributeValue(null, "installer"); + + systemStr = parser.getAttributeValue(null, "flags"); + if (systemStr != null) { + try { + pkgFlags = Integer.parseInt(systemStr); + } catch (NumberFormatException e) { + } + } else { + // For backward compatibility + systemStr = parser.getAttributeValue(null, "system"); + if (systemStr != null) { + pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM + : 0; + } else { + // Old settings that don't specify system... just treat + // them as system, good enough. + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } + } + String timeStampStr = parser.getAttributeValue(null, "ft"); + if (timeStampStr != null) { + try { + timeStamp = Long.parseLong(timeStampStr, 16); + } catch (NumberFormatException e) { + } + } else { + timeStampStr = parser.getAttributeValue(null, "ts"); + if (timeStampStr != null) { + try { + timeStamp = Long.parseLong(timeStampStr); + } catch (NumberFormatException e) { + } + } + } + timeStampStr = parser.getAttributeValue(null, "it"); + if (timeStampStr != null) { + try { + firstInstallTime = Long.parseLong(timeStampStr, 16); + } catch (NumberFormatException e) { + } + } + timeStampStr = parser.getAttributeValue(null, "ut"); + if (timeStampStr != null) { + try { + lastUpdateTime = Long.parseLong(timeStampStr, 16); + } catch (NumberFormatException e) { + } + } + if (PackageManagerService.DEBUG_SETTINGS) + Log.v(PackageManagerService.TAG, "Reading package: " + name + " userId=" + idStr + + " sharedUserId=" + sharedIdStr); + int userId = idStr != null ? Integer.parseInt(idStr) : 0; + if (resourcePathStr == null) { + resourcePathStr = codePathStr; + } + if (realName != null) { + realName = realName.intern(); + } + if (name == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <package> has no name at " + + parser.getPositionDescription()); + } else if (codePathStr == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <package> has no codePath at " + + parser.getPositionDescription()); + } else if (userId > 0) { + packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), + new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode, + pkgFlags); + if (PackageManagerService.DEBUG_SETTINGS) + Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" + + userId + " pkg=" + packageSetting); + if (packageSetting == null) { + PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding uid " + + userId + " while parsing settings at " + + parser.getPositionDescription()); + } else { + packageSetting.setTimeStamp(timeStamp); + packageSetting.firstInstallTime = firstInstallTime; + packageSetting.lastUpdateTime = lastUpdateTime; + } + } else if (sharedIdStr != null) { + userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; + if (userId > 0) { + packageSetting = new PendingPackage(name.intern(), realName, new File( + codePathStr), new File(resourcePathStr), nativeLibraryPathStr, userId, + versionCode, pkgFlags); + packageSetting.setTimeStamp(timeStamp); + packageSetting.firstInstallTime = firstInstallTime; + packageSetting.lastUpdateTime = lastUpdateTime; + mPendingPackages.add((PendingPackage) packageSetting); + if (PackageManagerService.DEBUG_SETTINGS) + Log.i(PackageManagerService.TAG, "Reading package " + name + + ": sharedUserId=" + userId + " pkg=" + packageSetting); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + + " has bad sharedId " + sharedIdStr + " at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + " has bad userId " + + idStr + " at " + parser.getPositionDescription()); + } + } catch (NumberFormatException e) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + " has bad userId " + + idStr + " at " + parser.getPositionDescription()); + } + if (packageSetting != null) { + packageSetting.uidError = "true".equals(uidError); + packageSetting.installerPackageName = installerPackageName; + packageSetting.nativeLibraryPathString = nativeLibraryPathStr; + final String enabledStr = parser.getAttributeValue(null, "enabled"); + if (enabledStr != null) { + if (enabledStr.equalsIgnoreCase("true")) { + packageSetting.enabled = COMPONENT_ENABLED_STATE_ENABLED; + } else if (enabledStr.equalsIgnoreCase("false")) { + packageSetting.enabled = COMPONENT_ENABLED_STATE_DISABLED; + } else if (enabledStr.equalsIgnoreCase("default")) { + packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT; + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + + " has bad enabled value: " + idStr + " at " + + parser.getPositionDescription()); + } + } else { + packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT; + } + final String installStatusStr = parser.getAttributeValue(null, "installStatus"); + if (installStatusStr != null) { + if (installStatusStr.equalsIgnoreCase("false")) { + packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_INCOMPLETE; + } else { + packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE; + } + } + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("disabled-components")) { + readDisabledComponentsLPw(packageSetting, parser); + } else if (tagName.equals("enabled-components")) { + readEnabledComponentsLPw(packageSetting, parser); + } else if (tagName.equals("sigs")) { + packageSetting.signatures.readXml(parser, mPastSignatures); + } else if (tagName.equals("perms")) { + readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions); + packageSetting.permissionsFixed = true; + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <package>: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + private void readDisabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + packageSetting.disabledComponents.add(name.intern()); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <disabled-components> has" + + " no name at " + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <disabled-components>: " + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readEnabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + packageSetting.enabledComponents.add(name.intern()); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <enabled-components> has" + + " no name at " + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <enabled-components>: " + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readSharedUserLPw(XmlPullParser parser) throws XmlPullParserException, IOException { + String name = null; + String idStr = null; + int pkgFlags = 0; + SharedUserSetting su = null; + try { + name = parser.getAttributeValue(null, "name"); + idStr = parser.getAttributeValue(null, "userId"); + int userId = idStr != null ? Integer.parseInt(idStr) : 0; + if ("true".equals(parser.getAttributeValue(null, "system"))) { + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } + if (name == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <shared-user> has no name at " + + parser.getPositionDescription()); + } else if (userId == 0) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: shared-user " + name + + " has bad userId " + idStr + " at " + + parser.getPositionDescription()); + } else { + if ((su = addSharedUserLPw(name.intern(), userId, pkgFlags)) == null) { + PackageManagerService + .reportSettingsProblem(Log.ERROR, "Occurred while parsing settings at " + + parser.getPositionDescription()); + } + } + } catch (NumberFormatException e) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + " has bad userId " + + idStr + " at " + parser.getPositionDescription()); + } + ; + + if (su != null) { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("sigs")) { + su.signatures.readXml(parser, mPastSignatures); + } else if (tagName.equals("perms")) { + readGrantedPermissionsLPw(parser, su.grantedPermissions); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <shared-user>: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + private void readGrantedPermissionsLPw(XmlPullParser parser, HashSet<String> outPerms) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + outPerms.add(name.intern()); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <perms> has" + " no name at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <perms>: " + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readPreferredActivitiesLPw(XmlPullParser parser) throws XmlPullParserException, + IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + PreferredActivity pa = new PreferredActivity(parser); + if (pa.mPref.getParseError() == null) { + mPreferredActivities.addFilter(pa); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: <preferred-activity> " + + pa.mPref.getParseError() + " at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <preferred-activities>: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + + // Returns -1 if we could not find an available UserId to assign + private int newUserIdLPw(Object obj) { + // Let's be stupidly inefficient for now... + final int N = mUserIds.size(); + for (int i = 0; i < N; i++) { + if (mUserIds.get(i) == null) { + mUserIds.set(i, obj); + return PackageManagerService.FIRST_APPLICATION_UID + i; + } + } + + // None left? + if (N >= PackageManagerService.MAX_APPLICATION_UIDS) { + return -1; + } + + mUserIds.add(obj); + return PackageManagerService.FIRST_APPLICATION_UID + N; + } + + public PackageSetting getDisabledSystemPkgLPr(String name) { + PackageSetting ps = mDisabledSysPackages.get(name); + return ps; + } + + boolean isEnabledLPr(ComponentInfo componentInfo, int flags) { + if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { + return true; + } + final PackageSetting packageSettings = mPackages.get(componentInfo.packageName); + if (PackageManagerService.DEBUG_SETTINGS) { + Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = " + componentInfo.packageName + + " componentName = " + componentInfo.name); + Log.v(PackageManagerService.TAG, "enabledComponents: " + + Arrays.toString(packageSettings.enabledComponents.toArray())); + Log.v(PackageManagerService.TAG, "disabledComponents: " + + Arrays.toString(packageSettings.disabledComponents.toArray())); + } + if (packageSettings == null) { + return false; + } + if (packageSettings.enabled == COMPONENT_ENABLED_STATE_DISABLED + || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled + && packageSettings.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) { + return false; + } + if (packageSettings.enabledComponents.contains(componentInfo.name)) { + return true; + } + if (packageSettings.disabledComponents.contains(componentInfo.name)) { + return false; + } + return componentInfo.enabled; + } + + String getInstallerPackageNameLPr(String packageName) { + final PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + return pkg.installerPackageName; + } + + int getApplicationEnabledSettingLPr(String packageName) { + final PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + return pkg.enabled; + } + + int getComponentEnabledSettingLPr(ComponentName componentName) { + final String packageName = componentName.getPackageName(); + final PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown component: " + componentName); + } + final String classNameStr = componentName.getClassName(); + return pkg.getCurrentEnabledStateLPr(classNameStr); + } + + boolean setPackageStoppedStateLPw(String packageName, boolean stopped, + boolean allowedByPermission, int uid) { + final PackageSetting pkgSetting = mPackages.get(packageName); + if (pkgSetting == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (!allowedByPermission && (uid != pkgSetting.userId)) { + throw new SecurityException( + "Permission Denial: attempt to change stopped state from pid=" + + Binder.getCallingPid() + + ", uid=" + uid + ", package uid=" + pkgSetting.userId); + } + if (DEBUG_STOPPED) { + if (stopped) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(TAG, "Stopping package " + packageName, e); + } + } + if (pkgSetting.stopped != stopped) { + pkgSetting.stopped = stopped; + pkgSetting.pkg.mSetStopped = stopped; + if (pkgSetting.notLaunched) { + if (pkgSetting.installerPackageName != null) { + PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, + pkgSetting.name, null, + pkgSetting.installerPackageName, null); + } + pkgSetting.notLaunched = false; + } + return true; + } + return false; + } + + void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState) { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + final Date date = new Date(); + boolean printedSomething = false; + for (final PackageSetting ps : mPackages.values()) { + if (packageName != null && !packageName.equals(ps.realName) + && !packageName.equals(ps.name)) { + continue; + } + + if (packageName != null) { + dumpState.setSharedUser(ps.sharedUser); + } + + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(" "); + pw.println("Packages:"); + printedSomething = true; + } + pw.print(" Package ["); + pw.print(ps.realName != null ? ps.realName : ps.name); + pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(ps))); + pw.println("):"); + + if (ps.realName != null) { + pw.print(" compat name="); + pw.println(ps.name); + } + + pw.print(" userId="); pw.print(ps.userId); + pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids)); + pw.print(" sharedUser="); pw.println(ps.sharedUser); + pw.print(" pkg="); pw.println(ps.pkg); + pw.print(" codePath="); pw.println(ps.codePathString); + pw.print(" resourcePath="); pw.println(ps.resourcePathString); + pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString); + pw.print(" versionCode="); pw.println(ps.versionCode); + if (ps.pkg != null) { + pw.print(" versionName="); pw.println(ps.pkg.mVersionName); + pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir); + pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion); + if (ps.pkg.mOperationPending) { + pw.println(" mOperationPending=true"); + } + pw.print(" supportsScreens=["); + boolean first = true; + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) { + if (!first) + pw.print(", "); + first = false; + pw.print("small"); + } + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) { + if (!first) + pw.print(", "); + first = false; + pw.print("medium"); + } + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { + if (!first) + pw.print(", "); + first = false; + pw.print("large"); + } + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) { + if (!first) + pw.print(", "); + first = false; + pw.print("xlarge"); + } + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) { + if (!first) + pw.print(", "); + first = false; + pw.print("resizeable"); + } + if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) { + if (!first) + pw.print(", "); + first = false; + pw.print("anyDensity"); + } + } + pw.println("]"); + pw.print(" timeStamp="); + date.setTime(ps.timeStamp); + pw.println(sdf.format(date)); + pw.print(" firstInstallTime="); + date.setTime(ps.firstInstallTime); + pw.println(sdf.format(date)); + pw.print(" lastUpdateTime="); + date.setTime(ps.lastUpdateTime); + pw.println(sdf.format(date)); + if (ps.installerPackageName != null) { + pw.print(" installerPackageName="); pw.println(ps.installerPackageName); + } + pw.print(" signatures="); pw.println(ps.signatures); + pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); + pw.print(" haveGids="); pw.println(ps.haveGids); + pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags)); + pw.print(" installStatus="); pw.print(ps.installStatus); + pw.print(" stopped="); pw.print(ps.stopped); + pw.print(" enabled="); pw.println(ps.enabled); + if (ps.disabledComponents.size() > 0) { + pw.println(" disabledComponents:"); + for (String s : ps.disabledComponents) { + pw.print(" "); pw.println(s); + } + } + if (ps.enabledComponents.size() > 0) { + pw.println(" enabledComponents:"); + for (String s : ps.enabledComponents) { + pw.print(" "); pw.println(s); + } + } + if (ps.grantedPermissions.size() > 0) { + pw.println(" grantedPermissions:"); + for (String s : ps.grantedPermissions) { + pw.print(" "); pw.println(s); + } + } + } + + printedSomething = false; + if (mRenamedPackages.size() > 0) { + for (final HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) { + if (packageName != null && !packageName.equals(e.getKey()) + && !packageName.equals(e.getValue())) { + continue; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(" "); + pw.println("Renamed packages:"); + printedSomething = true; + } + pw.print(" "); + pw.print(e.getKey()); + pw.print(" -> "); + pw.println(e.getValue()); + } + } + + printedSomething = false; + if (mDisabledSysPackages.size() > 0) { + for (final PackageSetting ps : mDisabledSysPackages.values()) { + if (packageName != null && !packageName.equals(ps.realName) + && !packageName.equals(ps.name)) { + continue; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(" "); + pw.println("Hidden system packages:"); + printedSomething = true; + } + pw.print(" Package ["); + pw.print(ps.realName != null ? ps.realName : ps.name); + pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(ps))); + pw.println("):"); + if (ps.realName != null) { + pw.print(" compat name="); + pw.println(ps.name); + } + pw.print(" userId="); + pw.println(ps.userId); + pw.print(" sharedUser="); + pw.println(ps.sharedUser); + pw.print(" codePath="); + pw.println(ps.codePathString); + pw.print(" resourcePath="); + pw.println(ps.resourcePathString); + } + } + } + + void dumpPermissionsLPr(PrintWriter pw, String packageName, DumpState dumpState) { + boolean printedSomething = false; + for (BasePermission p : mPermissions.values()) { + if (packageName != null && !packageName.equals(p.sourcePackage)) { + continue; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(" "); + pw.println("Permissions:"); + printedSomething = true; + } + pw.print(" Permission ["); pw.print(p.name); pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(p))); + pw.println("):"); + pw.print(" sourcePackage="); pw.println(p.sourcePackage); + pw.print(" uid="); pw.print(p.uid); + pw.print(" gids="); pw.print(PackageManagerService.arrayToString(p.gids)); + pw.print(" type="); pw.print(p.type); + pw.print(" prot="); pw.println(p.protectionLevel); + if (p.packageSetting != null) { + pw.print(" packageSetting="); pw.println(p.packageSetting); + } + if (p.perm != null) { + pw.print(" perm="); pw.println(p.perm); + } + } + } + + void dumpSharedUsersLPr(PrintWriter pw, String packageName, DumpState dumpState) { + boolean printedSomething = false; + for (SharedUserSetting su : mSharedUsers.values()) { + if (packageName != null && su != dumpState.getSharedUser()) { + continue; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(" "); + pw.println("Shared users:"); + printedSomething = true; + } + pw.print(" SharedUser ["); + pw.print(su.name); + pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(su))); + pw.println("):"); + pw.print(" userId="); + pw.print(su.userId); + pw.print(" gids="); + pw.println(PackageManagerService.arrayToString(su.gids)); + pw.println(" grantedPermissions:"); + for (String s : su.grantedPermissions) { + pw.print(" "); + pw.println(s); + } + } + } + + void dumpReadMessagesLPr(PrintWriter pw, DumpState dumpState) { + pw.println("Settings parse messages:"); + pw.print(mReadMessages.toString()); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/pm/SharedUserSetting.java b/services/java/com/android/server/pm/SharedUserSetting.java new file mode 100644 index 000000000000..76826eab2f07 --- /dev/null +++ b/services/java/com/android/server/pm/SharedUserSetting.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import java.util.HashSet; + +/** + * Settings data for a particular shared user ID we know about. + */ +final class SharedUserSetting extends GrantedPermissions { + final String name; + + int userId; + + final HashSet<PackageSetting> packages = new HashSet<PackageSetting>(); + + final PackageSignatures signatures = new PackageSignatures(); + + SharedUserSetting(String _name, int _pkgFlags) { + super(_pkgFlags); + name = _name; + } + + @Override + public String toString() { + return "SharedUserSetting{" + Integer.toHexString(System.identityHashCode(this)) + " " + + name + "/" + userId + "}"; + } +} diff --git a/services/jni/Android.mk b/services/jni/Android.mk index be37d5d56d9b..4e93fe2bfa0b 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -34,7 +34,7 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libinput \ libskia \ - libsurfaceflinger_client \ + libgui \ libusbhost ifeq ($(TARGET_SIMULATOR),true) diff --git a/services/jni/com_android_server_InputApplication.cpp b/services/jni/com_android_server_InputApplication.cpp index e64ec4ee4c8c..1f80242ceacc 100644 --- a/services/jni/com_android_server_InputApplication.cpp +++ b/services/jni/com_android_server_InputApplication.cpp @@ -26,8 +26,6 @@ namespace android { static struct { - jclass clazz; - jfieldID inputApplicationHandle; jfieldID name; jfieldID dispatchingTimeoutNanos; @@ -69,25 +67,25 @@ void android_server_InputApplication_toNative( #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputApplication(JNIEnv* env) { - FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/wm/InputApplication"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputApplication"); GET_FIELD_ID(gInputApplicationClassInfo.inputApplicationHandle, - gInputApplicationClassInfo.clazz, + clazz, "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); - GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz, + GET_FIELD_ID(gInputApplicationClassInfo.name, clazz, "name", "Ljava/lang/String;"); GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos, - gInputApplicationClassInfo.clazz, + clazz, "dispatchingTimeoutNanos", "J"); return 0; } diff --git a/services/jni/com_android_server_InputApplicationHandle.cpp b/services/jni/com_android_server_InputApplicationHandle.cpp index 3a1214ff9655..951696430235 100644 --- a/services/jni/com_android_server_InputApplicationHandle.cpp +++ b/services/jni/com_android_server_InputApplicationHandle.cpp @@ -26,8 +26,6 @@ namespace android { static struct { - jclass clazz; - jfieldID ptr; } gInputApplicationHandleClassInfo; @@ -98,8 +96,7 @@ static JNINativeMethod gInputApplicationHandleMethods[] = { #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ @@ -110,9 +107,10 @@ int register_android_server_InputApplicationHandle(JNIEnv* env) { gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - FIND_CLASS(gInputApplicationHandleClassInfo.clazz, "com/android/server/wm/InputApplicationHandle"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputApplicationHandle"); - GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, gInputApplicationHandleClassInfo.clazz, + GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz, "ptr", "I"); return 0; diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 80dddc2869d1..c0221231c176 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -52,8 +52,6 @@ namespace android { static struct { - jclass clazz; - jmethodID notifyConfigurationChanged; jmethodID notifyLidSwitchChanged; jmethodID notifyInputChannelBroken; @@ -95,16 +93,12 @@ static struct { } gInputDeviceClassInfo; static struct { - jclass clazz; - jfieldID touchscreen; jfieldID keyboard; jfieldID navigation; } gConfigurationClassInfo; static struct { - jclass clazz; - jfieldID bitmap; jfieldID hotSpotX; jfieldID hotSpotY; @@ -606,6 +600,7 @@ void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationO android_server_InputApplication_toNative(env, applicationObj, &application); if (application.inputApplicationHandle != NULL) { mInputManager->getDispatcher()->setFocusedApplication(&application); + return; } } mInputManager->getDispatcher()->setFocusedApplication(NULL); @@ -1024,15 +1019,14 @@ static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jcla return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent( & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis); } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) { - MotionEvent motionEvent; - status_t status = android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent); - if (status) { + const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj); + if (!motionEvent) { jniThrowRuntimeException(env, "Could not read contents of MotionEvent object."); return INPUT_EVENT_INJECTION_FAILED; } return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent( - & motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis); + motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis); } else { jniThrowRuntimeException(env, "Invalid input event type."); return INPUT_EVENT_INJECTION_FAILED; @@ -1226,8 +1220,7 @@ static JNINativeMethod gInputManagerMethods[] = { #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ var = env->GetMethodID(clazz, methodName, methodDescriptor); \ @@ -1244,77 +1237,82 @@ int register_android_server_InputManager(JNIEnv* env) { // Callbacks - FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/wm/InputManager$Callbacks"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputManager$Callbacks"); - GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, clazz, "notifyConfigurationChanged", "(J)V"); - GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, clazz, "notifyLidSwitchChanged", "(JZ)V"); - GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, clazz, "notifyInputChannelBroken", "(Lcom/android/server/wm/InputWindowHandle;)V"); - GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.notifyANR, clazz, "notifyANR", "(Lcom/android/server/wm/InputApplicationHandle;Lcom/android/server/wm/InputWindowHandle;)J"); - GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, clazz, "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff, - gCallbacksClassInfo.clazz, + clazz, "interceptMotionBeforeQueueingWhenScreenOff", "(I)I"); - GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, clazz, "interceptKeyBeforeDispatching", "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Z"); - GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, clazz, "dispatchUnhandledKey", "(Lcom/android/server/wm/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); - GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, clazz, "checkInjectEventsPermission", "(II)Z"); - GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, clazz, "filterTouchEvents", "()Z"); - GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, clazz, "filterJumpyTouchEvents", "()Z"); - GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyQuietTimeMillis, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyQuietTimeMillis, clazz, "getVirtualKeyQuietTimeMillis", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, clazz, "getExcludedDeviceNames", "()[Ljava/lang/String;"); - GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatTimeout, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatTimeout, clazz, "getKeyRepeatTimeout", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz, "getKeyRepeatDelay", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz, "getMaxEventsPerSecond", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getPointerLayer, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getPointerLayer, clazz, "getPointerLayer", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz, + GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, clazz, "getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;"); // KeyEvent FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); + gKeyEventClassInfo.clazz = jclass(env->NewGlobalRef(gKeyEventClassInfo.clazz)); + // MotionEvent FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); + gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz)); // InputDevice FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice"); + gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz)); GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz, "<init>", "()V"); @@ -1336,28 +1334,28 @@ int register_android_server_InputManager(JNIEnv* env) { // Configuration - FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration"); + FIND_CLASS(clazz, "android/content/res/Configuration"); - GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz, + GET_FIELD_ID(gConfigurationClassInfo.touchscreen, clazz, "touchscreen", "I"); - GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz, + GET_FIELD_ID(gConfigurationClassInfo.keyboard, clazz, "keyboard", "I"); - GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz, + GET_FIELD_ID(gConfigurationClassInfo.navigation, clazz, "navigation", "I"); // PointerIcon - FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/wm/InputManager$PointerIcon"); + FIND_CLASS(clazz, "com/android/server/wm/InputManager$PointerIcon"); - GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz, + GET_FIELD_ID(gPointerIconClassInfo.bitmap, clazz, "bitmap", "Landroid/graphics/Bitmap;"); - GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, gPointerIconClassInfo.clazz, + GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, clazz, "hotSpotX", "F"); - GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, gPointerIconClassInfo.clazz, + GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, clazz, "hotSpotY", "F"); return 0; diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp index 8548b47de606..99f625ce621b 100644 --- a/services/jni/com_android_server_InputWindow.cpp +++ b/services/jni/com_android_server_InputWindow.cpp @@ -28,8 +28,6 @@ namespace android { static struct { - jclass clazz; - jfieldID inputWindowHandle; jfieldID inputChannel; jfieldID name; @@ -136,71 +134,71 @@ void android_server_InputWindow_toNative( #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputWindow(JNIEnv* env) { - FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/wm/InputWindow"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputWindow"); - GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.inputWindowHandle, clazz, "inputWindowHandle", "Lcom/android/server/wm/InputWindowHandle;"); - GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.inputChannel, clazz, "inputChannel", "Landroid/view/InputChannel;"); - GET_FIELD_ID(gInputWindowClassInfo.name, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.name, clazz, "name", "Ljava/lang/String;"); - GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, clazz, "layoutParamsFlags", "I"); - GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, clazz, "layoutParamsType", "I"); - GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, clazz, "dispatchingTimeoutNanos", "J"); - GET_FIELD_ID(gInputWindowClassInfo.frameLeft, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.frameLeft, clazz, "frameLeft", "I"); - GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.frameTop, clazz, "frameTop", "I"); - GET_FIELD_ID(gInputWindowClassInfo.frameRight, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.frameRight, clazz, "frameRight", "I"); - GET_FIELD_ID(gInputWindowClassInfo.frameBottom, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.frameBottom, clazz, "frameBottom", "I"); - GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, clazz, "touchableRegion", "Landroid/graphics/Region;"); - GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.visible, clazz, "visible", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, clazz, "canReceiveKeys", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.hasFocus, clazz, "hasFocus", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, clazz, "hasWallpaper", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.paused, clazz, "paused", "Z"); - GET_FIELD_ID(gInputWindowClassInfo.layer, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.layer, clazz, "layer", "I"); - GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.ownerPid, clazz, "ownerPid", "I"); - GET_FIELD_ID(gInputWindowClassInfo.ownerUid, gInputWindowClassInfo.clazz, + GET_FIELD_ID(gInputWindowClassInfo.ownerUid, clazz, "ownerUid", "I"); return 0; } diff --git a/services/jni/com_android_server_InputWindowHandle.cpp b/services/jni/com_android_server_InputWindowHandle.cpp index 5b74e435c7ee..aaf679c62c1b 100644 --- a/services/jni/com_android_server_InputWindowHandle.cpp +++ b/services/jni/com_android_server_InputWindowHandle.cpp @@ -27,8 +27,6 @@ namespace android { static struct { - jclass clazz; - jfieldID ptr; jfieldID inputApplicationHandle; } gInputWindowHandleClassInfo; @@ -108,8 +106,7 @@ static JNINativeMethod gInputWindowHandleMethods[] = { #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ @@ -120,13 +117,14 @@ int register_android_server_InputWindowHandle(JNIEnv* env) { gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); - FIND_CLASS(gInputWindowHandleClassInfo.clazz, "com/android/server/wm/InputWindowHandle"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/wm/InputWindowHandle"); - GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, gInputWindowHandleClassInfo.clazz, + GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz, "ptr", "I"); GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle, - gInputWindowHandleClassInfo.clazz, + clazz, "inputApplicationHandle", "Lcom/android/server/wm/InputApplicationHandle;"); return 0; diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp index 705be6003d98..a389c114752f 100644 --- a/services/jni/com_android_server_PowerManagerService.cpp +++ b/services/jni/com_android_server_PowerManagerService.cpp @@ -35,8 +35,6 @@ namespace android { // ---------------------------------------------------------------------------- static struct { - jclass clazz; - jmethodID goToSleep; jmethodID userActivity; } gPowerManagerServiceClassInfo; @@ -144,8 +142,7 @@ static JNINativeMethod gPowerManagerServiceMethods[] = { #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); + LOG_FATAL_IF(! var, "Unable to find class " className); #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ var = env->GetMethodID(clazz, methodName, methodDescriptor); \ @@ -162,12 +159,13 @@ int register_android_server_PowerManagerService(JNIEnv* env) { // Callbacks - FIND_CLASS(gPowerManagerServiceClassInfo.clazz, "com/android/server/PowerManagerService"); + jclass clazz; + FIND_CLASS(clazz, "com/android/server/PowerManagerService"); - GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, gPowerManagerServiceClassInfo.clazz, + GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, clazz, "goToSleep", "(J)V"); - GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz, + GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, clazz, "userActivity", "(JZIZ)V"); // Initialize diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk index 7e17fdd58ede..c50e4a16bdcf 100644 --- a/services/sensorservice/Android.mk +++ b/services/sensorservice/Android.mk @@ -27,7 +27,7 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libgui -LOCAL_PRELINK_MODULE := false + LOCAL_MODULE:= libsensorservice diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 8a00a2ed57e7..9daaad8ea18c 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -41,7 +41,7 @@ LOCAL_SHARED_LIBRARIES := \ libGLESv1_CM \ libbinder \ libui \ - libsurfaceflinger_client + libgui LOCAL_C_INCLUDES := \ $(call include-path-for, corecg graphics) diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 517c335925a5..c4027e0f8703 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -52,6 +52,7 @@ template <typename T> inline T min(T a, T b) { Layer::Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client) : LayerBaseClient(flinger, display, client), + mFormat(PIXEL_FORMAT_NONE), mGLExtensions(GLExtensions::getInstance()), mNeedsBlending(true), mNeedsDithering(false), @@ -59,7 +60,8 @@ Layer::Layer(SurfaceFlinger* flinger, mProtectedByApp(false), mTextureManager(), mBufferManager(mTextureManager), - mWidth(0), mHeight(0), mNeedsScaling(false), mFixedSize(false) + mWidth(0), mHeight(0), + mNeedsScaling(false), mFixedSize(false) { } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index a9fa1ef42443..ea283c606af8 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -600,7 +600,7 @@ sp<FreezeLock> SurfaceFlinger::getFreezeLock() const } void SurfaceFlinger::computeVisibleRegions( - LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) + const LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) { const GraphicPlane& plane(graphicPlane(0)); const Transform& planeTransform(plane.transform()); @@ -735,8 +735,7 @@ void SurfaceFlinger::commitTransaction() void SurfaceFlinger::handlePageFlip() { bool visibleRegions = mVisibleRegionsDirty; - LayerVector& currentLayers( - const_cast<LayerVector&>(mDrawingState.layersSortedByZ)); + const LayerVector& currentLayers(mDrawingState.layersSortedByZ); visibleRegions |= lockPageFlip(currentLayers); const DisplayHardware& hw = graphicPlane(0).displayHardware(); @@ -748,9 +747,8 @@ void SurfaceFlinger::handlePageFlip() /* * rebuild the visible layer list */ + const size_t count = currentLayers.size(); mVisibleLayersSortedByZ.clear(); - const LayerVector& currentLayers(mDrawingState.layersSortedByZ); - size_t count = currentLayers.size(); mVisibleLayersSortedByZ.setCapacity(count); for (size_t i=0 ; i<count ; i++) { if (!currentLayers[i]->visibleRegionScreen.isEmpty()) @@ -2515,7 +2513,7 @@ ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const } break; } - if (++name >= SharedBufferStack::NUM_LAYERS_MAX) + if (++name >= int32_t(SharedBufferStack::NUM_LAYERS_MAX)) name = NO_MEMORY; } while(name >= 0); @@ -2562,7 +2560,7 @@ sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h void GraphicBufferAlloc::freeAllGraphicBuffersExcept(int bufIdx) { Mutex::Autolock _l(mLock); - if (0 <= bufIdx && bufIdx < mBuffers.size()) { + if (bufIdx >= 0 && size_t(bufIdx) < mBuffers.size()) { sp<GraphicBuffer> b(mBuffers[bufIdx]); mBuffers.clear(); mBuffers.add(b); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 95668190f456..0964848edf94 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -304,7 +304,7 @@ private: Vector< sp<LayerBase> >& ditchedLayers); void computeVisibleRegions( - LayerVector& currentLayers, + const LayerVector& currentLayers, Region& dirtyRegion, Region& wormholeRegion); @@ -371,7 +371,6 @@ private: // access must be protected by mStateLock mutable Mutex mStateLock; State mCurrentState; - State mDrawingState; volatile int32_t mTransactionFlags; volatile int32_t mTransactionCount; Condition mTransactionCV; @@ -395,6 +394,7 @@ private: // Can only accessed from the main thread, these members // don't need synchronization + State mDrawingState; Region mDirtyRegion; Region mDirtyRegionRemovedLayer; Region mInvalidRegion; diff --git a/services/surfaceflinger/tests/resize/Android.mk b/services/surfaceflinger/tests/resize/Android.mk index 24c2d01f538d..d81679eab73c 100644 --- a/services/surfaceflinger/tests/resize/Android.mk +++ b/services/surfaceflinger/tests/resize/Android.mk @@ -8,7 +8,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libui \ - libsurfaceflinger_client + libgui LOCAL_MODULE:= test-resize diff --git a/services/surfaceflinger/tests/screencap/Android.mk b/services/surfaceflinger/tests/screencap/Android.mk index 1cfb4716ed94..5cdd1a841c52 100644 --- a/services/surfaceflinger/tests/screencap/Android.mk +++ b/services/surfaceflinger/tests/screencap/Android.mk @@ -10,7 +10,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libskia \ libui \ - libsurfaceflinger_client + libgui LOCAL_MODULE:= test-screencap diff --git a/services/surfaceflinger/tests/surface/Android.mk b/services/surfaceflinger/tests/surface/Android.mk index ce0e8072f744..c59060e17357 100644 --- a/services/surfaceflinger/tests/surface/Android.mk +++ b/services/surfaceflinger/tests/surface/Android.mk @@ -9,7 +9,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libbinder \ libui \ - libsurfaceflinger_client + libgui LOCAL_MODULE:= test-surface |