diff options
146 files changed, 3926 insertions, 1479 deletions
diff --git a/Android.mk b/Android.mk index 7a3c46dc8e41..a69170f4db66 100644 --- a/Android.mk +++ b/Android.mk @@ -219,6 +219,8 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \ core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \ core/java/android/hardware/location/IContextHubCallback.aidl \ + core/java/android/hardware/location/IContextHubClient.aidl \ + core/java/android/hardware/location/IContextHubClientCallback.aidl \ core/java/android/hardware/location/IContextHubService.aidl \ core/java/android/hardware/location/IContextHubTransactionCallback.aidl \ core/java/android/hardware/radio/IRadioService.aidl \ @@ -527,11 +529,13 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/ims/internal/IImsService.aidl \ telephony/java/com/android/ims/internal/IImsServiceController.aidl \ telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl \ + telephony/java/com/android/ims/internal/IImsSmsFeature.aidl \ telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl \ telephony/java/com/android/ims/internal/IImsUt.aidl \ telephony/java/com/android/ims/internal/IImsUtListener.aidl \ telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl \ telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl \ + telephony/java/com/android/ims/internal/ISmsListener.aidl \ telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl \ telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl \ telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl \ @@ -1114,7 +1118,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS += \ -federate SupportLib https://developer.android.com \ -federationapi SupportLib prebuilts/sdk/current/support-api.txt -# ==== the api diff =========================== +# ==== Public API diff =========================== include $(CLEAR_VARS) LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES) @@ -1139,6 +1143,31 @@ LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_API_F include $(BUILD_APIDIFF) +# ==== System API diff =========================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES) +LOCAL_INTERMEDIATE_SOURCES := $(framework_docs_LOCAL_INTERMEDIATE_SOURCES) +LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \ + $(INTERNAL_PLATFORM_SYSTEM_API_FILE) + +LOCAL_MODULE := offline-system-sdk-referenceonly + +last_released_sdk_version := $(lastword $(call numerically_sort, \ + $(filter-out current, \ + $(patsubst $(SRC_SYSTEM_API_DIR)/%.txt,%, $(wildcard $(SRC_SYSTEM_API_DIR)/*.txt)) \ + )\ + )) + +LOCAL_APIDIFF_OLDAPI := $(LOCAL_PATH)/../../$(SRC_SYSTEM_API_DIR)/$(last_released_sdk_version) +LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_SYSTEM_API_FILE)) + +include $(BUILD_APIDIFF) + # ==== the api stubs and current.xml =========================== include $(CLEAR_VARS) @@ -1315,7 +1344,7 @@ $(full_target): $(static_doc_index_redirect) $(full_target): $(framework_built) -# ==== static html in the sdk ================================== +# ==== Public API static reference docs ================================== include $(CLEAR_VARS) LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES) @@ -1357,6 +1386,50 @@ $(full_target): $(static_doc_properties) $(full_target): $(framework_built) +# ==== System API static reference docs ================================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES) +LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES) +LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) +LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) +LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) +LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) +LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) +LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) + +LOCAL_MODULE := offline-system-sdk-referenceonly + +LOCAL_DROIDDOC_OPTIONS:=\ + $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ + -hide 101 -hide 104 -hide 108 \ + -showAnnotation android.annotation.SystemApi \ + -offlinemode \ + -title "Android System SDK" \ + -proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \ + -sdkvalues $(OUT_DOCS) \ + -hdf android.whichdoc offline \ + -referenceonly + +LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk + +include $(BUILD_DROIDDOC) + +static_doc_index_redirect := $(out_dir)/index.html +$(static_doc_index_redirect): $(LOCAL_PATH)/docs/docs-documentation-redirect.html + $(copy-file-to-target) + +static_doc_properties := $(out_dir)/source.properties +$(static_doc_properties): \ + $(LOCAL_PATH)/docs/source.properties | $(ACP) + $(hide) mkdir -p $(dir $@) + $(hide) $(ACP) $< $@ + +$(full_target): $(static_doc_index_redirect) +$(full_target): $(static_doc_properties) +$(full_target): $(framework_built) + + # ==== docs for the web (on the androiddevdocs app engine server) ======================= include $(CLEAR_VARS) diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java index b81908c34263..b9ee613315af 100644 --- a/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java +++ b/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java @@ -80,11 +80,11 @@ public class PaintMeasureTextTest { } while (state.keepRunning()) { - state.pauseTiming(); if (mCacheMode == DONT_USE_CACHE) { + state.pauseTiming(); Canvas.freeTextLayoutCaches(); + state.resumeTiming(); } - state.resumeTiming(); paint.measureText(mText); } diff --git a/api/current.txt b/api/current.txt index 7c2249143296..182dd3a33bfe 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22033,6 +22033,7 @@ package android.media { method public android.media.Image acquireLatestImage(); method public android.media.Image acquireNextImage(); method public void close(); + method public void discardFreeBuffers(); method public int getHeight(); method public int getImageFormat(); method public int getMaxImages(); @@ -33139,6 +33140,7 @@ package android.provider { public final class AlarmClock { ctor public AlarmClock(); field public static final java.lang.String ACTION_DISMISS_ALARM = "android.intent.action.DISMISS_ALARM"; + field public static final java.lang.String ACTION_DISMISS_TIMER = "android.intent.action.DISMISS_TIMER"; field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM"; field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER"; field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS"; @@ -47906,6 +47908,7 @@ package android.view.accessibility { method public void interrupt(); method public static boolean isAccessibilityButtonSupported(); method public boolean isEnabled(); + method public boolean isObservedEventType(int); method public boolean isTouchExplorationEnabled(); method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer); method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener); @@ -49091,15 +49094,19 @@ package android.view.textclassifier { public abstract interface TextClassifier { method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.view.textclassifier.TextClassification.Options); + method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int); method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList); method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options); + method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence); method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options); + method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int); method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList); field public static final android.view.textclassifier.TextClassifier NO_OP; field public static final java.lang.String TYPE_ADDRESS = "address"; field public static final java.lang.String TYPE_EMAIL = "email"; field public static final java.lang.String TYPE_OTHER = "other"; field public static final java.lang.String TYPE_PHONE = "phone"; + field public static final java.lang.String TYPE_UNKNOWN = ""; field public static final java.lang.String TYPE_URL = "url"; } @@ -49115,13 +49122,9 @@ package android.view.textclassifier { } public static final class TextLinks.Options { + ctor public TextLinks.Options(); method public android.os.LocaleList getDefaultLocales(); - } - - public static final class TextLinks.Options.Builder { - ctor public TextLinks.Options.Builder(); - method public android.view.textclassifier.TextLinks.Options build(); - method public android.view.textclassifier.TextLinks.Options.Builder setLocaleList(android.os.LocaleList); + method public android.view.textclassifier.TextLinks.Options setDefaultLocales(android.os.LocaleList); } public static final class TextLinks.TextLink { diff --git a/api/test-current.txt b/api/test-current.txt index 8647ed397c49..b18153885867 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -958,6 +958,15 @@ package android.view { package android.view.accessibility { + public final class AccessibilityManager { + method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler); + method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener); + } + + public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener { + method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager); + } + public class AccessibilityNodeInfo implements android.os.Parcelable { method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger); } diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 31722815276c..6ded24648353 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -159,7 +159,7 @@ int main(int argc, char** argv) void const* mapbase = MAP_FAILED; ssize_t mapsize = -1; - void* base = NULL; + void const* base = NULL; uint32_t w, s, h, f; android_dataspace d; size_t size = 0; @@ -179,6 +179,7 @@ int main(int argc, char** argv) ProcessState::self()->setThreadPoolMaxThreadCount(0); ProcessState::self()->startThreadPool(); + ScreenshotClient screenshot; sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId); if (display == NULL) { fprintf(stderr, "Unable to get handle for display %d\n", displayId); @@ -198,57 +199,51 @@ int main(int argc, char** argv) uint8_t displayOrientation = configs[activeConfig].orientation; uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation]; - sp<GraphicBuffer> outBuffer; - status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */, - 0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation, - &outBuffer); - if (result != NO_ERROR) { - close(fd); - _exit(1); - } - - result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base); - - if (base == NULL) { - close(fd); - _exit(1); + status_t result = screenshot.update(display, Rect(), + 0 /* reqWidth */, 0 /* reqHeight */, + INT32_MIN, INT32_MAX, /* all layers */ + false, captureOrientation); + if (result == NO_ERROR) { + base = screenshot.getPixels(); + w = screenshot.getWidth(); + h = screenshot.getHeight(); + s = screenshot.getStride(); + f = screenshot.getFormat(); + d = screenshot.getDataSpace(); + size = screenshot.getSize(); } - w = outBuffer->getWidth(); - h = outBuffer->getHeight(); - s = outBuffer->getStride(); - f = outBuffer->getPixelFormat(); - d = HAL_DATASPACE_UNKNOWN; - size = s * h * bytesPerPixel(f); - - if (png) { - const SkImageInfo info = - SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, dataSpaceToColorSpace(d)); - SkPixmap pixmap(info, base, s * bytesPerPixel(f)); - struct FDWStream final : public SkWStream { - size_t fBytesWritten = 0; - int fFd; - FDWStream(int f) : fFd(f) {} - size_t bytesWritten() const override { return fBytesWritten; } - bool write(const void* buffer, size_t size) override { - fBytesWritten += size; - return size == 0 || ::write(fFd, buffer, size) > 0; - } - } fdStream(fd); - (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100); - if (fn != NULL) { - notifyMediaScanner(fn); - } - } else { - uint32_t c = dataSpaceToInt(d); - write(fd, &w, 4); - write(fd, &h, 4); - write(fd, &f, 4); - write(fd, &c, 4); - size_t Bpp = bytesPerPixel(f); - for (size_t y=0 ; y<h ; y++) { - write(fd, base, w*Bpp); - base = (void *)((char *)base + s*Bpp); + if (base != NULL) { + if (png) { + const SkImageInfo info = + SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, + dataSpaceToColorSpace(d)); + SkPixmap pixmap(info, base, s * bytesPerPixel(f)); + struct FDWStream final : public SkWStream { + size_t fBytesWritten = 0; + int fFd; + FDWStream(int f) : fFd(f) {} + size_t bytesWritten() const override { return fBytesWritten; } + bool write(const void* buffer, size_t size) override { + fBytesWritten += size; + return size == 0 || ::write(fFd, buffer, size) > 0; + } + } fdStream(fd); + (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100); + if (fn != NULL) { + notifyMediaScanner(fn); + } + } else { + uint32_t c = dataSpaceToInt(d); + write(fd, &w, 4); + write(fd, &h, 4); + write(fd, &f, 4); + write(fd, &c, 4); + size_t Bpp = bytesPerPixel(f); + for (size_t y=0 ; y<h ; y++) { + write(fd, base, w*Bpp); + base = (void *)((char *)base + s*Bpp); + } } } close(fd); @@ -258,4 +253,4 @@ int main(int argc, char** argv) // b/36066697: Avoid running static destructors. _exit(0); -}
\ No newline at end of file +} diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 57b4fc1e24b3..2fd794777cb6 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -220,7 +220,7 @@ void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, const unique_ptr<MetricsManager>& metricsManager) { std::lock_guard<std::mutex> lock(mBroadcastTimesMutex); - size_t totalBytes = metricsManager->byteSize(); + size_t totalBytes = metricsManager->byteSize() + mUidMap->getBytesUsed(); if (totalBytes > .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data. auto lastFlushNs = mLastBroadcastTimes.find(key); if (lastFlushNs != mLastBroadcastTimes.end()) { diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index c019d5d31443..7eca5aa21201 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -480,7 +480,7 @@ status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) { fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(), mProcessor->GetMetricsSize(key)); } - fprintf(out, "Detailed statsd stats in logcat..."); + fprintf(out, "Detailed statsd stats in logcat...\n"); StatsdStats& statsdStats = StatsdStats::getInstance(); bool reset = false; if (args.size() > 1) { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 20e7c600938e..e0d9ce7d8802 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -91,6 +91,8 @@ message Atom { CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008; CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009; CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010; + WifiActivityEnergyInfoPulled wifi_activity_energy_info_pulled = 1011; + ModemActivityInfoPulled modem_activity_info_pulled = 1012; } } @@ -699,7 +701,7 @@ message SettingChanged { optional int32 user = 7; } -/* +/** * Logs activity going to foreground or background * * Logged from: @@ -839,7 +841,7 @@ message KernelWakelockPulled { optional int64 time = 4; } -/* +/** * Pulls PowerStatePlatformSleepState. * * Definition here: @@ -899,7 +901,7 @@ message IsolatedUidChanged { optional int32 is_create = 3; } -/* +/** * Pulls Cpu time per frequency. * Note: this should be pulled for gauge metric only, without condition. * The puller keeps internal state of last values. It should not be pulled by @@ -916,7 +918,7 @@ message CpuTimePerFreqPulled { optional uint64 time = 3; } -/* +/** * Pulls Cpu Time Per Uid. * Note that isolated process uid time should be attributed to host uids. */ @@ -965,3 +967,56 @@ message PacketWakeupOccurred { // The destination port if this was a TCP or UDP packet. optional int32 destination_port = 9; } + +/** + * Pulls Wifi Controller Activity Energy Info + */ +message WifiActivityEnergyInfoPulled { + // timestamp(wall clock) of record creation + optional uint64 timestamp_ms = 1; + // stack reported state + // TODO: replace this with proto enum + optional int32 stack_state = 2; + // tx time in ms + optional uint64 controller_tx_time_ms = 3; + // rx time in ms + optional uint64 controller_rx_time_ms = 4; + // idle time in ms + optional uint64 controller_idle_time_ms = 5; + // product of current(mA), voltage(V) and time(ms) + optional uint64 controller_energy_used = 6; +} + +/** + * Pulls Modem Activity Energy Info + */ +message ModemActivityInfoPulled { + // timestamp(wall clock) of record creation + optional uint64 timestamp_ms = 1; + // sleep time in ms. + optional uint64 sleep_time_ms = 2; + // idle time in ms + optional uint64 controller_idle_time_ms = 3; + /** + * Tx power index + * index 0 = tx_power < 0dBm + * index 1 = 0dBm < tx_power < 5dBm + * index 2 = 5dBm < tx_power < 15dBm + * index 3 = 15dBm < tx_power < 20dBm + * index 4 = tx_power > 20dBm + */ + // tx time in ms at power level 0 + optional uint64 controller_tx_time_pl0_ms = 4; + // tx time in ms at power level 1 + optional uint64 controller_tx_time_pl1_ms = 5; + // tx time in ms at power level 2 + optional uint64 controller_tx_time_pl2_ms = 6; + // tx time in ms at power level 3 + optional uint64 controller_tx_time_pl3_ms = 7; + // tx time in ms at power level 4 + optional uint64 controller_tx_time_pl4_ms = 8; + // rx time in ms at power level 5 + optional uint64 controller_rx_time_ms = 9; + // product of current(mA), voltage(V) and time(ms) + optional uint64 energy_used = 10; +} diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 2bd36126d340..29574579b2a0 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -45,6 +45,7 @@ const int FIELD_ID_MATCHER_STATS = 4; const int FIELD_ID_CONDITION_STATS = 5; const int FIELD_ID_METRIC_STATS = 6; const int FIELD_ID_ATOM_STATS = 7; +const int FIELD_ID_UIDMAP_STATS = 8; const int FIELD_ID_MATCHER_STATS_NAME = 1; const int FIELD_ID_MATCHER_STATS_COUNT = 2; @@ -173,6 +174,27 @@ void StatsdStats::noteMetricsReportSent(const ConfigKey& key, int32_t timeSec) { it->second.add_dump_report_time_sec(timeSec); } +void StatsdStats::noteUidMapDropped(int snapshots, int deltas) { + lock_guard<std::mutex> lock(mLock); + mUidMapStats.set_dropped_snapshots(mUidMapStats.dropped_snapshots() + snapshots); + mUidMapStats.set_dropped_changes(mUidMapStats.dropped_changes() + deltas); +} + +void StatsdStats::setUidMapSnapshots(int snapshots) { + lock_guard<std::mutex> lock(mLock); + mUidMapStats.set_snapshots(snapshots); +} + +void StatsdStats::setUidMapChanges(int changes) { + lock_guard<std::mutex> lock(mLock); + mUidMapStats.set_changes(changes); +} + +void StatsdStats::setCurrentUidMapMemory(int bytes) { + lock_guard<std::mutex> lock(mLock); + mUidMapStats.set_bytes_used(bytes); +} + void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) { lock_guard<std::mutex> lock(mLock); // if name doesn't exist before, it will create the key with count 0. @@ -364,6 +386,15 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { } } + const int numBytes = mUidMapStats.ByteSize(); + vector<char> buffer(numBytes); + mUidMapStats.SerializeToArray(&buffer[0], numBytes); + proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size()); + VLOG("UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes " + "lost=%d", + mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(), + mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes()); + output->clear(); size_t bufferSize = proto.size(); output->resize(bufferSize); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 451144f80bbf..d6f6566cbc33 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -45,6 +45,10 @@ public: const static int kMaxTimestampCount = 20; + // Cap the UID map's memory usage to this. This should be fairly high since the UID information + // is critical for understanding the metrics. + const static size_t kMaxBytesUsedUidMap = 50 * 1024; + /** * Report a new config has been received and report the static stats about the config. * @@ -113,6 +117,18 @@ public: void noteAtomLogged(int atomId, int32_t timeSec); /** + * Records the number of snapshot and delta entries that are being dropped from the uid map. + */ + void noteUidMapDropped(int snapshots, int deltas); + + /** + * Updates the number of snapshots currently stored in the uid map. + */ + void setUidMapSnapshots(int snapshots); + void setUidMapChanges(int changes); + void setCurrentUidMapMemory(int bytes); + + /** * Reset the historical stats. Including all stats in icebox, and the tracked stats about * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue * to collect stats after reset() has been called. @@ -133,6 +149,9 @@ private: int32_t mStartTimeSec; + // Track the number of dropped entries used by the uid map. + StatsdStatsReport_UidMapStats mUidMapStats; + // The stats about the configs that are still in use. std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 4338399bf675..00642400b3e8 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -94,8 +94,6 @@ CountMetricProducer::~CountMetricProducer() { } void CountMetricProducer::startNewProtoOutputStream(long long startTime) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - mProto = std::make_unique<ProtoOutputStream>(); mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); @@ -109,8 +107,13 @@ void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); } -void CountMetricProducer::serializeBuckets() { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); +std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() { + long long endTime = time(nullptr) * NS_PER_SEC; + + // Dump current bucket if it's stale. + // If current bucket is still on-going, don't force dump current bucket. + // In finish(), We can force dump current bucket. + flushIfNeeded(endTime); VLOG("metric %s dump report now...", mMetric.name().c_str()); for (const auto& counter : mPastBuckets) { @@ -156,40 +159,28 @@ void CountMetricProducer::serializeBuckets() { } mProto->end(wrapperToken); } + mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); - mPastBuckets.clear(); - // TODO: Clear mDimensionKeyMap once the report is dumped. -} - -std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() { - long long endTime = time(nullptr) * NS_PER_SEC; VLOG("metric %s dump report now...", mMetric.name().c_str()); - // Dump current bucket if it's stale. - // If current bucket is still on-going, don't force dump current bucket. - // In finish(), We can force dump current bucket. - flushIfNeeded(endTime); - - // TODO(yanglu): merge these three functions to one to avoid three locks. - serializeBuckets(); - std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(endTime); + mPastBuckets.clear(); return buffer; + + // TODO: Clear mDimensionKeyMap once the report is dumped. } void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mCondition = conditionMet; } bool CountMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) { return false; } @@ -217,40 +208,38 @@ void CountMetricProducer::onMatchedLogEventInternal( flushIfNeeded(eventTimeNs); - // ===========GuardRail============== - if (hitGuardRail(eventKey)) { + if (condition == false) { return; } - // TODO(yanglu): move the following logic to a seperate function to make it lockable. - { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - if (condition == false) { + auto it = mCurrentSlicedCounter->find(eventKey); + + if (it == mCurrentSlicedCounter->end()) { + // ===========GuardRail============== + if (hitGuardRail(eventKey)) { return; } - auto it = mCurrentSlicedCounter->find(eventKey); - if (it == mCurrentSlicedCounter->end()) { - // create a counter for the new key - mCurrentSlicedCounter->insert({eventKey, 1}); - } else { - // increment the existing value - auto& count = it->second; - count++; - } - const int64_t& count = mCurrentSlicedCounter->find(eventKey)->second; - for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, count); - } - VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(), (long long)(count)); + // create a counter for the new key + (*mCurrentSlicedCounter)[eventKey] = 1; + } else { + // increment the existing value + auto& count = it->second; + count++; + } + + for (auto& tracker : mAnomalyTrackers) { + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, + mCurrentSlicedCounter->find(eventKey)->second); } + + VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(), + (long long)(*mCurrentSlicedCounter)[eventKey]); } // When a new matched event comes in, we check if event falls into the current // bucket. If not, flush the old counter to past buckets and initialize the new bucket. void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) { return; } @@ -284,7 +273,6 @@ void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { // greater than actual data size as it contains each dimension of // CountMetricData is duplicated. size_t CountMetricProducer::byteSize() const { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); size_t totalSize = 0; for (const auto& pair : mPastBuckets) { totalSize += pair.second.size() * kBucketSize; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 164dfb25c01e..f78a199de103 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -75,8 +75,6 @@ protected: void startNewProtoOutputStream(long long timestamp) override; private: - void serializeBuckets(); - const CountMetric mMetric; // TODO: Add a lock to mPastBuckets. diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 3b49b9aadcde..a0374c0ba67c 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -104,7 +104,6 @@ DurationMetricProducer::~DurationMetricProducer() { } void DurationMetricProducer::startNewProtoOutputStream(long long startTime) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mProto = std::make_unique<ProtoOutputStream>(); mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); @@ -112,7 +111,7 @@ void DurationMetricProducer::startNewProtoOutputStream(long long startTime) { } unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( - const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) const { + const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) { switch (mMetric.aggregation_type()) { case DurationMetric_AggregationType_SUM: return make_unique<OringDurationTracker>( @@ -131,7 +130,6 @@ void DurationMetricProducer::finish() { } void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); flushIfNeeded(eventTime); // Now for each of the on-going event, check if the condition has changed for them. @@ -141,7 +139,6 @@ void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime } void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); mCondition = conditionMet; flushIfNeeded(eventTime); @@ -152,8 +149,15 @@ void DurationMetricProducer::onConditionChanged(const bool conditionMet, const u } } -void DurationMetricProducer::SerializeBuckets() { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); +std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() { + long long endTime = time(nullptr) * NS_PER_SEC; + + // Dump current bucket if it's stale. + // If current bucket is still on-going, don't force dump current bucket. + // In finish(), We can force dump current bucket. + flushIfNeeded(endTime); + VLOG("metric %s dump report now...", mMetric.name().c_str()); + for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; VLOG(" dimension key %s", hashableKey.c_str()); @@ -210,29 +214,13 @@ void DurationMetricProducer::SerializeBuckets() { mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); -} - -std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() { - VLOG("metric %s dump report now...", mMetric.name().c_str()); - - long long endTime = time(nullptr) * NS_PER_SEC; - - // Dump current bucket if it's stale. - // If current bucket is still on-going, don't force dump current bucket. - // In finish(), We can force dump current bucket. - flushIfNeeded(endTime); - - SerializeBuckets(); - std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); - startNewProtoOutputStream(endTime); // TODO: Properly clear the old buckets. return buffer; } void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { return; } @@ -252,7 +240,6 @@ void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) { } bool DurationMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); // the key is not new, we are good. if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) { return false; @@ -278,37 +265,32 @@ void DurationMetricProducer::onMatchedLogEventInternal( const LogEvent& event, bool scheduledPull) { flushIfNeeded(event.GetTimestampNs()); - // TODO(yanglu): move the following logic to a seperate function to make it lockable. - { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - if (matcherIndex == mStopAllIndex) { - for (auto& pair : mCurrentSlicedDuration) { - pair.second->noteStopAll(event.GetTimestampNs()); - } - return; + if (matcherIndex == mStopAllIndex) { + for (auto& pair : mCurrentSlicedDuration) { + pair.second->noteStopAll(event.GetTimestampNs()); } + return; + } - HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension)); + HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension)); - if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) { - if (hitGuardRail(eventKey)) { - return; - } - mCurrentSlicedDuration[eventKey] = - createDurationTracker(eventKey, mPastBuckets[eventKey]); + if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) { + if (hitGuardRail(eventKey)) { + return; } - auto it = mCurrentSlicedDuration.find(eventKey); + mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]); + } - if (matcherIndex == mStartIndex) { - it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys); - } else if (matcherIndex == mStopIndex) { - it->second->noteStop(atomKey, event.GetTimestampNs(), false); - } + auto it = mCurrentSlicedDuration.find(eventKey); + + if (matcherIndex == mStartIndex) { + it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys); + } else if (matcherIndex == mStopIndex) { + it->second->noteStop(atomKey, event.GetTimestampNs(), false); } } size_t DurationMetricProducer::byteSize() const { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); size_t totalSize = 0; for (const auto& pair : mPastBuckets) { totalSize += pair.second.size() * kBucketSize; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 68fff484b64d..5b5373ec9aeb 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -71,8 +71,6 @@ protected: void startNewProtoOutputStream(long long timestamp) override; private: - void SerializeBuckets(); - const DurationMetric mMetric; // Index of the SimpleLogEntryMatcher which defines the start. @@ -98,8 +96,8 @@ private: std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>> mCurrentSlicedDuration; - std::unique_ptr<DurationTracker> createDurationTracker( - const HashableDimensionKey& eventKey, std::vector<DurationBucket>& bucket) const; + std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey, + std::vector<DurationBucket>& bucket); bool hitGuardRail(const HashableDimensionKey& newKey); static const size_t kBucketSize = sizeof(DurationBucket{}); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 53f112a507ce..95a18f7241f0 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -73,7 +73,6 @@ EventMetricProducer::~EventMetricProducer() { } void EventMetricProducer::startNewProtoOutputStream(long long startTime) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mProto = std::make_unique<ProtoOutputStream>(); // TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData, // and StatsEvent. @@ -90,16 +89,11 @@ void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() { long long endTime = time(nullptr) * NS_PER_SEC; - // TODO(yanglu): make this section to an util function. - { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - mProto->end(mProtoToken); - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime); - - size_t bufferSize = mProto->size(); - VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize); - } + mProto->end(mProtoToken); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime); + size_t bufferSize = mProto->size(); + VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(endTime); @@ -109,7 +103,6 @@ std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() { void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mCondition = conditionMet; } @@ -117,7 +110,6 @@ void EventMetricProducer::onMatchedLogEventInternal( const size_t matcherIndex, const HashableDimensionKey& eventKey, const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, const LogEvent& event, bool scheduledPull) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (!condition) { return; } @@ -132,7 +124,6 @@ void EventMetricProducer::onMatchedLogEventInternal( } size_t EventMetricProducer::byteSize() const { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); return mProto->bytesWritten(); } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index ed4c76083303..1791654ba7cc 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -102,7 +102,6 @@ GaugeMetricProducer::~GaugeMetricProducer() { } void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mProto = std::make_unique<ProtoOutputStream>(); mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); @@ -112,8 +111,14 @@ void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) { void GaugeMetricProducer::finish() { } -void GaugeMetricProducer::SerializeBuckets() { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); +std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() { + VLOG("gauge metric %s dump report now...", mMetric.name().c_str()); + + // Dump current bucket if it's stale. + // If current bucket is still on-going, don't force dump current bucket. + // In finish(), We can force dump current bucket. + flushIfNeeded(time(nullptr) * NS_PER_SEC); + for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; auto it = mDimensionKeyMap.find(hashableKey); @@ -161,69 +166,50 @@ void GaugeMetricProducer::SerializeBuckets() { mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); - mPastBuckets.clear(); -} - -std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() { - VLOG("gauge metric %s dump report now...", mMetric.name().c_str()); - - // Dump current bucket if it's stale. - // If current bucket is still on-going, don't force dump current bucket. - // In finish(), We can force dump current bucket. - flushIfNeeded(time(nullptr) * NS_PER_SEC); - - SerializeBuckets(); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(time(nullptr) * NS_PER_SEC); + mPastBuckets.clear(); + return buffer; // TODO: Clear mDimensionKeyMap once the report is dumped. } void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { + AutoMutex _l(mLock); VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); - - // flushIfNeeded holds the write lock and is thread-safe. flushIfNeeded(eventTime); + mCondition = conditionMet; + // Push mode. No need to proactively pull the gauge data. + if (mPullTagId == -1) { + return; + } + if (!mCondition) { + return; + } + // Already have gauge metric for the current bucket, do not do it again. + if (mCurrentSlicedBucket->size() > 0) { + return; + } vector<std::shared_ptr<LogEvent>> allData; - // The following section is to update the condition and re-pull the gauge. - // TODO(yanglu): make it a seperate lockable function. - { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - - mCondition = conditionMet; - - // Push mode. No need to proactively pull the gauge data. - if (mPullTagId == -1) { - return; - } - if (!mCondition) { - return; - } - // Already have gauge metric for the current bucket, do not do it again. - if (mCurrentSlicedBucket->size() > 0) { - return; - } - if (!mStatsPullerManager.Pull(mPullTagId, &allData)) { - ALOGE("Stats puller failed for tag: %d", mPullTagId); - return; - } + if (!mStatsPullerManager.Pull(mPullTagId, &allData)) { + ALOGE("Stats puller failed for tag: %d", mPullTagId); + return; } - - // onMatchedLogEventInternal holds the write lock and is thread-safe. for (const auto& data : allData) { onMatchedLogEvent(0, *data, false /*scheduledPull*/); } + flushIfNeeded(eventTime); } void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); } -int64_t GaugeMetricProducer::getGauge(const LogEvent& event) const { +int64_t GaugeMetricProducer::getGauge(const LogEvent& event) { status_t err = NO_ERROR; int64_t val = event.GetLong(mMetric.gauge_field(), &err); if (err == NO_ERROR) { @@ -235,14 +221,13 @@ int64_t GaugeMetricProducer::getGauge(const LogEvent& event) const { } void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { - // onMatchedLogEventInternal holds the write lock and is thread-safe. + AutoMutex mutex(mLock); for (const auto& data : allData) { onMatchedLogEvent(0, *data, true /*scheduledPull*/); } } bool GaugeMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) { return false; } @@ -266,32 +251,32 @@ void GaugeMetricProducer::onMatchedLogEventInternal( const size_t matcherIndex, const HashableDimensionKey& eventKey, const map<string, HashableDimensionKey>& conditionKey, bool condition, const LogEvent& event, bool scheduledPull) { - uint64_t eventTimeNs = event.GetTimestampNs(); - flushIfNeeded(eventTimeNs); - if (condition == false) { return; } - const long gauge = getGauge(event); - if (gauge < 0) { - return; - } - if (hitGuardRail(eventKey)) { - return; - } - - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + uint64_t eventTimeNs = event.GetTimestampNs(); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); return; } + // When the event happens in a new bucket, flush the old buckets. + if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) { + flushIfNeeded(eventTimeNs); + } + // For gauge metric, we just simply use the first gauge in the given bucket. if (!mCurrentSlicedBucket->empty()) { return; } + const long gauge = getGauge(event); + if (gauge >= 0) { + if (hitGuardRail(eventKey)) { + return; + } (*mCurrentSlicedBucket)[eventKey] = gauge; + } for (auto& tracker : mAnomalyTrackers) { tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge); } @@ -303,7 +288,6 @@ void GaugeMetricProducer::onMatchedLogEventInternal( // if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside // the GaugeMetricProducer while holding the lock. void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) { return; } @@ -337,7 +321,6 @@ void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { } size_t GaugeMetricProducer::byteSize() const { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); size_t totalSize = 0; for (const auto& pair : mPastBuckets) { totalSize += pair.second.size() * kBucketSize; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 8df61117d75d..f344303179e8 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -81,8 +81,6 @@ protected: void startNewProtoOutputStream(long long timestamp) override; private: - void SerializeBuckets(); - // The default bucket size for gauge metric is 1 second. static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000; const GaugeMetric mMetric; @@ -91,6 +89,8 @@ private: // tagId for pulled data. -1 if this is not pulled const int mPullTagId; + Mutex mLock; + // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets; @@ -98,7 +98,7 @@ private: // The current bucket. std::shared_ptr<DimToValMap> mCurrentSlicedBucket = std::make_shared<DimToValMap>(); - int64_t getGauge(const LogEvent& event) const; + int64_t getGauge(const LogEvent& event); bool hitGuardRail(const HashableDimensionKey& newKey); diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 7542a9439032..62fb632faaaf 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -23,7 +23,6 @@ using std::map; void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event, bool scheduledPull) { - std::unique_lock<std::shared_timed_mutex> writeLock(mRWMutex); uint64_t eventTimeNs = event.GetTimestampNs(); // this is old event, maybe statsd restarted? if (eventTimeNs < mStartTimeNs) { @@ -60,16 +59,12 @@ void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent } else { condition = mCondition; } - // Unlock as onMatchedLogEventInternal is threadsafe. - writeLock.unlock(); onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event, scheduledPull); } std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProto() { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - size_t bufferSize = mProto->size(); std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize)); diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 27343ad6215a..b22ff6f3348c 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -17,8 +17,6 @@ #ifndef METRIC_PRODUCER_H #define METRIC_PRODUCER_H -#include <shared_mutex> - #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" #include "config/ConfigKey.h" @@ -139,10 +137,6 @@ protected: long long mProtoToken; - // Read/Write mutex to make the producer thread-safe. - // TODO(yanglu): replace with std::shared_mutex when available in libc++. - mutable std::shared_timed_mutex mRWMutex; - virtual void startNewProtoOutputStream(long long timestamp) = 0; std::unique_ptr<std::vector<uint8_t>> serializeProto(); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index eed7841ab435..66c8419de662 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -121,7 +121,6 @@ ValueMetricProducer::~ValueMetricProducer() { } void ValueMetricProducer::startNewProtoOutputStream(long long startTime) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mProto = std::make_unique<ProtoOutputStream>(); mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); @@ -137,8 +136,9 @@ void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); } -void ValueMetricProducer::SerializeBuckets() { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); +std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() { + VLOG("metric %s dump report now...", mMetric.name().c_str()); + for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; VLOG(" dimension key %s", hashableKey.c_str()); @@ -185,63 +185,47 @@ void ValueMetricProducer::SerializeBuckets() { mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); - mPastBuckets.clear(); -} -std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() { VLOG("metric %s dump report now...", mMetric.name().c_str()); - - SerializeBuckets(); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(time(nullptr) * NS_PER_SEC); + mPastBuckets.clear(); return buffer; + // TODO: Clear mDimensionKeyMap once the report is dumped. } void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) { - vector<shared_ptr<LogEvent>> allData; - - // TODO(yanglu): move the following logic to a seperate function to make it lockable. - { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - mCondition = condition; - - if (mPullTagId == -1) { - return; - } + AutoMutex _l(mLock); + mCondition = condition; + if (mPullTagId != -1) { if (mCondition == true) { mStatsPullerManager->RegisterReceiver(mPullTagId, this, mMetric.bucket().bucket_size_millis()); } else if (mCondition == false) { mStatsPullerManager->UnRegisterReceiver(mPullTagId, this); } - if (!mStatsPullerManager->Pull(mPullTagId, &allData)) { - return; - } - } - if (allData.size() == 0) { + vector<shared_ptr<LogEvent>> allData; + if (mStatsPullerManager->Pull(mPullTagId, &allData)) { + if (allData.size() == 0) { + return; + } + for (const auto& data : allData) { + onMatchedLogEvent(0, *data, false); + } + flushIfNeeded(eventTime); + } return; } - - // onMatchedLogEventInternal holds the write lock and is thread-safe. - for (const auto& data : allData) { - onMatchedLogEvent(0, *data, false); - } - // flushIfNeeded holds the write lock and is thread-safe. - flushIfNeeded(eventTime); -} - -bool ValueMetricProducer::IsConditionMet() const { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); - return mCondition == true || !mMetric.has_condition(); } void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { - if (IsConditionMet()) { + AutoMutex _l(mLock); + if (mCondition == true || !mMetric.has_condition()) { if (allData.size() == 0) { return; } @@ -258,7 +242,6 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven } bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); // ===========GuardRail============== // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) { @@ -279,75 +262,58 @@ bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { return false; } -void ValueMetricProducer::onMatchedLogEventInternal_pull(const uint64_t& eventTimeNs, - const HashableDimensionKey& eventKey, - const long& value, bool scheduledPull) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - +void ValueMetricProducer::onMatchedLogEventInternal( + const size_t matcherIndex, const HashableDimensionKey& eventKey, + const map<string, HashableDimensionKey>& conditionKey, bool condition, + const LogEvent& event, bool scheduledPull) { + uint64_t eventTimeNs = event.GetTimestampNs(); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); return; } - Interval& interval = mCurrentSlicedBucket[eventKey]; - if (scheduledPull) { - // scheduled pull always sets beginning of current bucket and end - // of next bucket - if (interval.raw.size() > 0) { - interval.raw.back().second = value; - } else { - interval.raw.push_back(make_pair(value, value)); - } - Interval& nextInterval = mNextSlicedBucket[eventKey]; - if (nextInterval.raw.size() == 0) { - nextInterval.raw.push_back(make_pair(value, 0)); - } else { - nextInterval.raw.front().first = value; - } - } else { - if (mCondition == true) { - interval.raw.push_back(make_pair(value, 0)); - } else { - if (interval.raw.size() != 0) { - interval.raw.back().second = value; - } else { - interval.tainted = true; - VLOG("Data on condition true missing!"); - } - } - } -} -void ValueMetricProducer::onMatchedLogEventInternal_push(const uint64_t& eventTimeNs, - const HashableDimensionKey& eventKey, - const long& value) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); - if (eventTimeNs < mCurrentBucketStartTimeNs) { - VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, - (long long)mCurrentBucketStartTimeNs); + if (hitGuardRail(eventKey)) { return; } - mCurrentSlicedBucket[eventKey].raw.push_back(make_pair(value, 0)); -} + Interval& interval = mCurrentSlicedBucket[eventKey]; -void ValueMetricProducer::onMatchedLogEventInternal( - const size_t matcherIndex, const HashableDimensionKey& eventKey, - const map<string, HashableDimensionKey>& conditionKey, bool condition, - const LogEvent& event, bool scheduledPull) { - uint64_t eventTimeNs = event.GetTimestampNs(); long value = get_value(event); - if (hitGuardRail(eventKey)) { - return; - } + if (mPullTagId != -1) { - onMatchedLogEventInternal_pull(eventTimeNs, eventKey, value, scheduledPull); + if (scheduledPull) { + // scheduled pull always sets beginning of current bucket and end + // of next bucket + if (interval.raw.size() > 0) { + interval.raw.back().second = value; + } else { + interval.raw.push_back(make_pair(value, value)); + } + Interval& nextInterval = mNextSlicedBucket[eventKey]; + if (nextInterval.raw.size() == 0) { + nextInterval.raw.push_back(make_pair(value, 0)); + } else { + nextInterval.raw.front().first = value; + } + } else { + if (mCondition == true) { + interval.raw.push_back(make_pair(value, 0)); + } else { + if (interval.raw.size() != 0) { + interval.raw.back().second = value; + } else { + interval.tainted = true; + VLOG("Data on condition true missing!"); + } + } + } } else { flushIfNeeded(eventTimeNs); - onMatchedLogEventInternal_push(eventTimeNs, eventKey, value); + interval.raw.push_back(make_pair(value, 0)); } } -long ValueMetricProducer::get_value(const LogEvent& event) const { +long ValueMetricProducer::get_value(const LogEvent& event) { status_t err = NO_ERROR; long val = event.GetLong(mMetric.value_field(), &err); if (err == NO_ERROR) { @@ -359,7 +325,6 @@ long ValueMetricProducer::get_value(const LogEvent& event) const { } void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { - std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) { VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs, (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs)); @@ -408,7 +373,6 @@ void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { } size_t ValueMetricProducer::byteSize() const { - std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); size_t totalSize = 0; for (const auto& pair : mPastBuckets) { totalSize += pair.second.size() * kBucketSize; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index e87e9dabcec2..a024bd804d83 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -72,16 +72,6 @@ protected: void startNewProtoOutputStream(long long timestamp) override; private: - void onMatchedLogEventInternal_pull(const uint64_t& eventTimeNs, - const HashableDimensionKey& eventKey, const long& value, - bool scheduledPull); - void onMatchedLogEventInternal_push(const uint64_t& eventTimeNs, - const HashableDimensionKey& eventKey, const long& value); - - void SerializeBuckets(); - - bool IsConditionMet() const; - const ValueMetric mMetric; std::shared_ptr<StatsPullerManager> mStatsPullerManager; @@ -92,6 +82,8 @@ private: const int pullTagId, const uint64_t startTimeNs, std::shared_ptr<StatsPullerManager> statsPullerManager); + Mutex mLock; + // tagId for pulled data. -1 if this is not pulled const int mPullTagId; @@ -110,7 +102,7 @@ private: // TODO: Add a lock to mPastBuckets. std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets; - long get_value(const LogEvent& event) const; + long get_value(const LogEvent& event); bool hitGuardRail(const HashableDimensionKey& newKey); diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 6c32d3e1b11e..db592e2c5841 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#define DEBUG true // STOPSHIP if true #include "Log.h" +#include "guardrail/StatsdStats.h" #include "packages/UidMap.h" +#include <android/os/IStatsCompanionService.h> +#include <binder/IServiceManager.h> #include <utils/Errors.h> using namespace android; @@ -26,6 +29,11 @@ namespace android { namespace os { namespace statsd { +UidMap::UidMap() : mBytesUsed(0) { +} +UidMap::~UidMap() { +} + bool UidMap::hasApp(int uid, const string& packageName) const { lock_guard<mutex> lock(mMutex); @@ -73,6 +81,10 @@ void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, t->set_version(int(versionCode[j])); t->set_uid(uid[j]); } + mBytesUsed += snapshot->ByteSize(); + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); + StatsdStats::getInstance().setUidMapSnapshots(mOutput.snapshots_size()); + ensureBytesUsedBelowLimit(); } void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) { @@ -96,6 +108,10 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i log->set_app(app); log->set_uid(uid); log->set_version(versionCode); + mBytesUsed += log->ByteSize(); + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); + StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size()); + ensureBytesUsedBelowLimit(); auto range = mMap.equal_range(int(uid)); for (auto it = range.first; it != range.second; ++it) { @@ -103,7 +119,7 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i it->second.versionCode = int(versionCode); return; } - ALOGD("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid); + VLOG("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid); return; } @@ -111,6 +127,28 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i mMap.insert(make_pair(uid, AppData(app, int(versionCode)))); } +void UidMap::ensureBytesUsedBelowLimit() { + size_t limit; + if (maxBytesOverride <= 0) { + limit = StatsdStats::kMaxBytesUsedUidMap; + } else { + limit = maxBytesOverride; + } + while (mBytesUsed > limit) { + VLOG("Bytes used %zu is above limit %zu, need to delete something", mBytesUsed, limit); + if (mOutput.snapshots_size() > 0) { + auto snapshots = mOutput.mutable_snapshots(); + snapshots->erase(snapshots->begin()); // Remove first snapshot. + StatsdStats::getInstance().noteUidMapDropped(1, 0); + } else if (mOutput.changes_size() > 0) { + auto changes = mOutput.mutable_changes(); + changes->DeleteSubrange(0, 1); + StatsdStats::getInstance().noteUidMapDropped(0, 1); + } + mBytesUsed = mOutput.ByteSize(); + } +} + void UidMap::removeApp(const String16& app_16, const int32_t& uid) { removeApp(time(nullptr) * NS_PER_SEC, app_16, uid); } @@ -128,6 +166,10 @@ void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const i log->set_timestamp_nanos(timestamp); log->set_app(app); log->set_uid(uid); + mBytesUsed += log->ByteSize(); + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); + StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size()); + ensureBytesUsedBelowLimit(); auto range = mMap.equal_range(int(uid)); for (auto it = range.first; it != range.second; ++it) { @@ -136,7 +178,7 @@ void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const i return; } } - ALOGD("removeApp failed to find the app %s with uid %i to remove", app.c_str(), uid); + VLOG("removeApp failed to find the app %s with uid %i to remove", app.c_str(), uid); return; } @@ -177,7 +219,6 @@ int UidMap::getParentUidOrSelf(int uid) { void UidMap::clearOutput() { mOutput.Clear(); - // Re-initialize the initial state for the outputs. This results in extra data being uploaded // but helps ensure we can re-construct the UID->app name, versionCode mapping in server. auto snapshot = mOutput.add_snapshots(); @@ -187,6 +228,12 @@ void UidMap::clearOutput() { t->set_version(it.second.versionCode); t->set_uid(it.first); } + + // Also update the guardrail trackers. + StatsdStats::getInstance().setUidMapChanges(0); + StatsdStats::getInstance().setUidMapSnapshots(1); + mBytesUsed = snapshot->ByteSize(); + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); } int64_t UidMap::getMinimumTimestampNs() { @@ -201,6 +248,10 @@ int64_t UidMap::getMinimumTimestampNs() { return m; } +size_t UidMap::getBytesUsed() { + return mBytesUsed; +} + UidMapping UidMap::getOutput(const ConfigKey& key) { return getOutput(time(nullptr) * NS_PER_SEC, key); } @@ -236,6 +287,10 @@ UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { } } } + mBytesUsed = mOutput.ByteSize(); // Compute actual size after potential deletions. + StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); + StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size()); + StatsdStats::getInstance().setUidMapSnapshots(mOutput.snapshots_size()); return ret; } @@ -250,6 +305,23 @@ void UidMap::printUidMap(FILE* out) { void UidMap::OnConfigUpdated(const ConfigKey& key) { mLastUpdatePerConfigKey[key] = -1; + + // Ensure there is at least one snapshot available since this configuration also needs to know + // what all the uid's represent. + if (mOutput.snapshots_size() == 0) { + sp<IStatsCompanionService> statsCompanion = nullptr; + // Get statscompanion service from service manager + const sp<IServiceManager> sm(defaultServiceManager()); + if (sm != nullptr) { + const String16 name("statscompanion"); + statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name)); + if (statsCompanion == nullptr) { + ALOGW("statscompanion service unavailable!"); + return; + } + statsCompanion->triggerUidSnapshot(); + } + } } void UidMap::OnConfigRemoved(const ConfigKey& key) { diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 24eb96619527..d2971c9b8a8d 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -50,15 +50,16 @@ struct AppData { // at any given moment. This map must be updated by StatsCompanionService. class UidMap : public virtual android::RefBase { public: + UidMap(); + ~UidMap(); + /* * All three inputs must be the same size, and the jth element in each array refers to the same * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j]. */ - // TODO: Add safeguards to call clearOutput if there's too much data already stored. void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode, const vector<String16>& packageName); - // TODO: Add safeguards to call clearOutput if there's too much data already stored. void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode); void removeApp(const String16& packageName, const int32_t& uid); @@ -98,11 +99,13 @@ public: // in case we lose a previous upload. void clearOutput(); + // Get currently cached value of memory used by UID map. + size_t getBytesUsed(); + private: void updateMap(const int64_t& timestamp, const vector<int32_t>& uid, const vector<int32_t>& versionCode, const vector<String16>& packageName); - // TODO: Add safeguards to call clearOutput if there's too much data already stored. void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid, const int32_t& versionCode); void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid); @@ -135,8 +138,22 @@ private: // Returns the minimum value from mConfigKeys. int64_t getMinimumTimestampNs(); + // If our current used bytes is above the limit, then we clear out the earliest snapshot. If + // there are no more snapshots, then we clear out the earliest delta. We repeat the deletions + // until the memory consumed by mOutput is below the specified limit. + void ensureBytesUsedBelowLimit(); + + // Override used for testing the max memory allowed by uid map. -1 means we use the value + // specified in StatsdStats.h with the rest of the guardrails. + size_t maxBytesOverride = -1; + + // Cache the size of mOutput; + size_t mBytesUsed; + // Allows unit-test to access private methods. FRIEND_TEST(UidMapTest, TestClearingOutput); + FRIEND_TEST(UidMapTest, TestMemoryComputed); + FRIEND_TEST(UidMapTest, TestMemoryGuardrail); }; } // namespace statsd diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index e7e1d43974af..cc8a26de1a8d 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -220,4 +220,13 @@ message StatsdStatsReport { } repeated AtomStats atom_stats = 7; + + message UidMapStats { + optional int32 snapshots = 1; + optional int32 changes = 2; + optional int32 bytes_used = 3; + optional int32 dropped_snapshots = 4; + optional int32 dropped_changes = 5; + } + optional UidMapStats uidmap_stats = 8; }
\ No newline at end of file diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index 0c194683884c..aa194e625060 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -15,6 +15,7 @@ #include "packages/UidMap.h" #include "StatsLogProcessor.h" #include "config/ConfigKey.h" +#include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" @@ -35,7 +36,8 @@ const string kApp2 = "app2.sharing.1"; TEST(UidMapTest, TestIsolatedUID) { sp<UidMap> m = new UidMap(); sp<AnomalyMonitor> anomalyMonitor; - StatsLogProcessor p(m, anomalyMonitor, nullptr); + // Construct the processor with a dummy sendBroadcast function that does nothing. + StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {}); LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); addEvent.write(100); // parent UID addEvent.write(101); // isolated UID @@ -114,26 +116,34 @@ TEST(UidMapTest, TestClearingOutput) { versions.push_back(4); versions.push_back(5); m.updateMap(1, uids, versions, apps); + EXPECT_EQ(1, m.mOutput.snapshots_size()); UidMapping results = m.getOutput(2, config1); EXPECT_EQ(1, results.snapshots_size()); // It should be cleared now + EXPECT_EQ(0, m.mOutput.snapshots_size()); results = m.getOutput(3, config1); EXPECT_EQ(0, results.snapshots_size()); // Now add another configuration. m.OnConfigUpdated(config2); m.updateApp(5, String16(kApp1.c_str()), 1000, 40); + EXPECT_EQ(1, m.mOutput.changes_size()); results = m.getOutput(6, config1); EXPECT_EQ(0, results.snapshots_size()); EXPECT_EQ(1, results.changes_size()); + EXPECT_EQ(1, m.mOutput.changes_size()); - // Now we still haven't been able to delete anything + // Add another delta update. m.updateApp(7, String16(kApp2.c_str()), 1001, 41); + EXPECT_EQ(2, m.mOutput.changes_size()); + + // We still can't remove anything. results = m.getOutput(8, config1); EXPECT_EQ(0, results.snapshots_size()); EXPECT_EQ(2, results.changes_size()); + EXPECT_EQ(2, m.mOutput.changes_size()); results = m.getOutput(9, config2); EXPECT_EQ(0, results.snapshots_size()); @@ -142,6 +152,66 @@ TEST(UidMapTest, TestClearingOutput) { EXPECT_EQ(0, m.mOutput.snapshots_size()); EXPECT_EQ(0, m.mOutput.changes_size()); } + +TEST(UidMapTest, TestMemoryComputed) { + UidMap m; + + ConfigKey config1(1, "config1"); + m.OnConfigUpdated(config1); + + size_t startBytes = m.mBytesUsed; + vector<int32_t> uids; + vector<int32_t> versions; + vector<String16> apps; + uids.push_back(1000); + apps.push_back(String16(kApp1.c_str())); + versions.push_back(1); + m.updateMap(1, uids, versions, apps); + size_t snapshot_bytes = m.mBytesUsed; + EXPECT_TRUE(snapshot_bytes > startBytes); + + m.updateApp(3, String16(kApp1.c_str()), 1000, 40); + EXPECT_TRUE(m.mBytesUsed > snapshot_bytes); + size_t bytesWithSnapshotChange = m.mBytesUsed; + + m.getOutput(2, config1); + EXPECT_TRUE(m.mBytesUsed < bytesWithSnapshotChange); + size_t prevBytes = m.mBytesUsed; + + m.getOutput(4, config1); + EXPECT_TRUE(m.mBytesUsed < prevBytes); +} + +TEST(UidMapTest, TestMemoryGuardrail) { + UidMap m; + string buf; + + ConfigKey config1(1, "config1"); + m.OnConfigUpdated(config1); + + size_t startBytes = m.mBytesUsed; + vector<int32_t> uids; + vector<int32_t> versions; + vector<String16> apps; + for (int i = 0; i < 100; i++) { + uids.push_back(1); + buf = "EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY." + to_string(i); + apps.push_back(String16(buf.c_str())); + versions.push_back(1); + } + m.updateMap(1, uids, versions, apps); + EXPECT_EQ(1, m.mOutput.snapshots_size()); + + m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2); + EXPECT_EQ(1, m.mOutput.snapshots_size()); + EXPECT_EQ(1, m.mOutput.changes_size()); + + // Now force deletion by limiting the memory to hold one delta change. + m.maxBytesOverride = 80; // Since the app string alone requires >45 characters. + m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4); + EXPECT_EQ(0, m.mOutput.snapshots_size()); + EXPECT_EQ(1, m.mOutput.changes_size()); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java index 5b59c21fcd45..fe3d86d4cd6a 100644 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java +++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java @@ -110,113 +110,7 @@ public class DisplayProtoUtils { log.getEventMetrics(); for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) { sb.append(getDateStr(event.getTimestampNanos())).append(": "); - switch (event.getAtom().getPushedCase()) { - case SETTING_CHANGED: - sb.append("SETTING_CHANGED\n"); - break; - case SYNC_STATE_CHANGED: - sb.append("SYNC_STATE_CHANGED\n"); - break; - case AUDIO_STATE_CHANGED: - sb.append("AUDIO_STATE_CHANGED\n"); - break; - case CAMERA_STATE_CHANGED: - sb.append("CAMERA_STATE_CHANGED\n"); - break; - case ISOLATED_UID_CHANGED: - sb.append("ISOLATED_UID_CHANGED\n"); - break; - case SCREEN_STATE_CHANGED: - sb.append("SCREEN_STATE_CHANGED\n"); - break; - case SENSOR_STATE_CHANGED: - sb.append("SENSOR_STATE_CHANGED\n"); - break; - case BATTERY_LEVEL_CHANGED: - sb.append("BATTERY_LEVEL_CHANGED\n"); - break; - case PLUGGED_STATE_CHANGED: - sb.append("PLUGGED_STATE_CHANGED\n"); - break; - case WAKEUP_ALARM_OCCURRED: - sb.append("WAKEUP_ALARM_OCCURRED\n"); - break; - case BLE_SCAN_STATE_CHANGED: - sb.append("BLE_SCAN_STATE_CHANGED\n"); - break; - case CHARGING_STATE_CHANGED: - sb.append("CHARGING_STATE_CHANGED\n"); - break; - case GPS_SCAN_STATE_CHANGED: - sb.append("GPS_SCAN_STATE_CHANGED\n"); - break; - case KERNEL_WAKEUP_REPORTED: - sb.append("KERNEL_WAKEUP_REPORTED\n"); - break; - case WAKELOCK_STATE_CHANGED: - sb.append("WAKELOCK_STATE_CHANGED\n"); - break; - case WIFI_LOCK_STATE_CHANGED: - sb.append("WIFI_LOCK_STATE_CHANGED\n"); - break; - case WIFI_SCAN_STATE_CHANGED: - sb.append("WIFI_SCAN_STATE_CHANGED\n"); - break; - case BLE_SCAN_RESULT_RECEIVED: - sb.append("BLE_SCAN_RESULT_RECEIVED\n"); - break; - case DEVICE_ON_STATUS_CHANGED: - sb.append("DEVICE_ON_STATUS_CHANGED\n"); - break; - case FLASHLIGHT_STATE_CHANGED: - sb.append("FLASHLIGHT_STATE_CHANGED\n"); - break; - case SCREEN_BRIGHTNESS_CHANGED: - sb.append("SCREEN_BRIGHTNESS_CHANGED\n"); - break; - case UID_PROCESS_STATE_CHANGED: - sb.append("UID_PROCESS_STATE_CHANGED\n"); - break; - case UID_WAKELOCK_STATE_CHANGED: - sb.append("UID_WAKELOCK_STATE_CHANGED\n"); - break; - case DEVICE_TEMPERATURE_REPORTED: - sb.append("DEVICE_TEMPERATURE_REPORTED\n"); - break; - case SCHEDULED_JOB_STATE_CHANGED: - sb.append("SCHEDULED_JOB_STATE_CHANGED\n"); - break; - case MEDIA_CODEC_ACTIVITY_CHANGED: - sb.append("MEDIA_CODEC_ACTIVITY_CHANGED\n"); - break; - case WIFI_SIGNAL_STRENGTH_CHANGED: - sb.append("WIFI_SIGNAL_STRENGTH_CHANGED\n"); - break; - case PHONE_SIGNAL_STRENGTH_CHANGED: - sb.append("PHONE_SIGNAL_STRENGTH_CHANGED\n"); - break; - case DEVICE_IDLE_MODE_STATE_CHANGED: - sb.append("DEVICE_IDLE_MODE_STATE_CHANGED\n"); - break; - case BATTERY_SAVER_MODE_STATE_CHANGED: - sb.append("BATTERY_SAVER_MODE_STATE_CHANGED\n"); - break; - case PROCESS_LIFE_CYCLE_STATE_CHANGED: - sb.append("PROCESS_LIFE_CYCLE_STATE_CHANGED\n"); - break; - case ACTIVITY_FOREGROUND_STATE_CHANGED: - sb.append("ACTIVITY_FOREGROUND_STATE_CHANGED\n"); - break; - case BLE_UNOPTIMIZED_SCAN_STATE_CHANGED: - sb.append("BLE_UNOPTIMIZED_SCAN_STATE_CHANGED\n"); - break; - case LONG_PARTIAL_WAKELOCK_STATE_CHANGED: - sb.append("LONG_PARTIAL_WAKELOCK_STATE_CHANGED\n"); - break; - case PUSHED_NOT_SET: - sb.append("PUSHED_NOT_SET\n"); - break; - } + sb.append(event.getAtom().getPushedCase().toString()).append("\n"); } } diff --git a/core/java/Android.bp b/core/java/Android.bp index d8c79293f9ba..b43cf277aa57 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -4,16 +4,16 @@ filegroup { } // only used by key_store_service -cc_library_static { +cc_library_shared { name: "libkeystore_aidl", srcs: ["android/security/IKeystoreService.aidl"], aidl: { export_aidl_headers: true, - include_dirs: ["frameworks/base/core/java/"], + include_dirs: [ + "frameworks/base/core/java/", + "system/security/keystore/", + ], }, - header_libs: [ - "libkeystore_headers", - ], shared_libs: [ "libbinder", "libcutils", @@ -22,7 +22,12 @@ cc_library_static { "libhidltransport", "libhwbinder", "liblog", + "libkeystore_parcelables", "libselinux", "libutils", ], + export_shared_lib_headers: [ + "libbinder", + "libkeystore_parcelables", + ], } diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 06a9b0676d08..e0d60cd059d0 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -16,6 +16,8 @@ package android.accessibilityservice; +import static android.content.pm.PackageManager.FEATURE_FINGERPRINT; + import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; @@ -47,8 +49,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static android.content.pm.PackageManager.FEATURE_FINGERPRINT; - /** * This class describes an {@link AccessibilityService}. The system notifies an * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s @@ -554,7 +554,7 @@ public class AccessibilityServiceInfo implements Parcelable { } /** - * Updates the properties that an AccessibilitySerivice can change dynamically. + * Updates the properties that an AccessibilityService can change dynamically. * * @param other The info from which to update the properties. * diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java index 6ea08252ac9a..8c47598fff34 100644 --- a/core/java/android/app/SharedPreferencesImpl.java +++ b/core/java/android/app/SharedPreferencesImpl.java @@ -34,8 +34,6 @@ import dalvik.system.BlockGuard; import libcore.io.IoUtils; -import com.google.android.collect.Maps; - import org.xmlpull.v1.XmlPullParserException; import java.io.BufferedInputStream; @@ -139,7 +137,7 @@ final class SharedPreferencesImpl implements SharedPreferences { Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission"); } - Map map = null; + Map<String, Object> map = null; StructStat stat = null; try { stat = Os.stat(mFile.getPath()); @@ -148,7 +146,7 @@ final class SharedPreferencesImpl implements SharedPreferences { try { str = new BufferedInputStream( new FileInputStream(mFile), 16*1024); - map = XmlUtils.readMapXml(str); + map = (Map<String, Object>) XmlUtils.readMapXml(str); } catch (Exception e) { Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e); } finally { @@ -214,12 +212,14 @@ final class SharedPreferencesImpl implements SharedPreferences { } } + @Override public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { synchronized(mLock) { mListeners.put(listener, CONTENT); } } + @Override public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { synchronized(mLock) { mListeners.remove(listener); @@ -241,6 +241,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } + @Override public Map<String, ?> getAll() { synchronized (mLock) { awaitLoadedLocked(); @@ -249,6 +250,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } + @Override @Nullable public String getString(String key, @Nullable String defValue) { synchronized (mLock) { @@ -258,6 +260,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } + @Override @Nullable public Set<String> getStringSet(String key, @Nullable Set<String> defValues) { synchronized (mLock) { @@ -267,6 +270,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } + @Override public int getInt(String key, int defValue) { synchronized (mLock) { awaitLoadedLocked(); @@ -274,6 +278,7 @@ final class SharedPreferencesImpl implements SharedPreferences { return v != null ? v : defValue; } } + @Override public long getLong(String key, long defValue) { synchronized (mLock) { awaitLoadedLocked(); @@ -281,6 +286,7 @@ final class SharedPreferencesImpl implements SharedPreferences { return v != null ? v : defValue; } } + @Override public float getFloat(String key, float defValue) { synchronized (mLock) { awaitLoadedLocked(); @@ -288,6 +294,7 @@ final class SharedPreferencesImpl implements SharedPreferences { return v != null ? v : defValue; } } + @Override public boolean getBoolean(String key, boolean defValue) { synchronized (mLock) { awaitLoadedLocked(); @@ -296,6 +303,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } + @Override public boolean contains(String key) { synchronized (mLock) { awaitLoadedLocked(); @@ -303,6 +311,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } + @Override public Editor edit() { // TODO: remove the need to call awaitLoadedLocked() when // requesting an editor. will require some work on the @@ -347,71 +356,81 @@ final class SharedPreferencesImpl implements SharedPreferences { } public final class EditorImpl implements Editor { - private final Object mLock = new Object(); + private final Object mEditorLock = new Object(); - @GuardedBy("mLock") - private final Map<String, Object> mModified = Maps.newHashMap(); + @GuardedBy("mEditorLock") + private final Map<String, Object> mModified = new HashMap<>(); - @GuardedBy("mLock") + @GuardedBy("mEditorLock") private boolean mClear = false; + @Override public Editor putString(String key, @Nullable String value) { - synchronized (mLock) { + synchronized (mEditorLock) { mModified.put(key, value); return this; } } + @Override public Editor putStringSet(String key, @Nullable Set<String> values) { - synchronized (mLock) { + synchronized (mEditorLock) { mModified.put(key, (values == null) ? null : new HashSet<String>(values)); return this; } } + @Override public Editor putInt(String key, int value) { - synchronized (mLock) { + synchronized (mEditorLock) { mModified.put(key, value); return this; } } + @Override public Editor putLong(String key, long value) { - synchronized (mLock) { + synchronized (mEditorLock) { mModified.put(key, value); return this; } } + @Override public Editor putFloat(String key, float value) { - synchronized (mLock) { + synchronized (mEditorLock) { mModified.put(key, value); return this; } } + @Override public Editor putBoolean(String key, boolean value) { - synchronized (mLock) { + synchronized (mEditorLock) { mModified.put(key, value); return this; } } + @Override public Editor remove(String key) { - synchronized (mLock) { + synchronized (mEditorLock) { mModified.put(key, this); return this; } } + @Override public Editor clear() { - synchronized (mLock) { + synchronized (mEditorLock) { mClear = true; return this; } } + @Override public void apply() { final long startTime = System.currentTimeMillis(); final MemoryCommitResult mcr = commitToMemory(); final Runnable awaitCommit = new Runnable() { + @Override public void run() { try { mcr.writtenToDiskLatch.await(); @@ -429,6 +448,7 @@ final class SharedPreferencesImpl implements SharedPreferences { QueuedWork.addFinisher(awaitCommit); Runnable postWriteRunnable = new Runnable() { + @Override public void run() { awaitCommit.run(); QueuedWork.removeFinisher(awaitCommit); @@ -471,13 +491,13 @@ final class SharedPreferencesImpl implements SharedPreferences { listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet()); } - synchronized (mLock) { + synchronized (mEditorLock) { boolean changesMade = false; if (mClear) { - if (!mMap.isEmpty()) { + if (!mapToWriteToDisk.isEmpty()) { changesMade = true; - mMap.clear(); + mapToWriteToDisk.clear(); } mClear = false; } @@ -489,18 +509,18 @@ final class SharedPreferencesImpl implements SharedPreferences { // setting a value to "null" for a given key is specified to be // equivalent to calling remove on that key. if (v == this || v == null) { - if (!mMap.containsKey(k)) { + if (!mapToWriteToDisk.containsKey(k)) { continue; } - mMap.remove(k); + mapToWriteToDisk.remove(k); } else { - if (mMap.containsKey(k)) { - Object existingValue = mMap.get(k); + if (mapToWriteToDisk.containsKey(k)) { + Object existingValue = mapToWriteToDisk.get(k); if (existingValue != null && existingValue.equals(v)) { continue; } } - mMap.put(k, v); + mapToWriteToDisk.put(k, v); } changesMade = true; @@ -522,6 +542,7 @@ final class SharedPreferencesImpl implements SharedPreferences { mapToWriteToDisk); } + @Override public boolean commit() { long startTime = 0; @@ -564,11 +585,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } else { // Run this function on the main thread. - ActivityThread.sMainThreadHandler.post(new Runnable() { - public void run() { - notifyListeners(mcr); - } - }); + ActivityThread.sMainThreadHandler.post(() -> notifyListeners(mcr)); } } } @@ -594,6 +611,7 @@ final class SharedPreferencesImpl implements SharedPreferences { final boolean isFromSyncCommit = (postWriteRunnable == null); final Runnable writeToDiskRunnable = new Runnable() { + @Override public void run() { synchronized (mWritingToDiskLock) { writeToFile(mcr, isFromSyncCommit); @@ -646,7 +664,7 @@ final class SharedPreferencesImpl implements SharedPreferences { return str; } - // Note: must hold mWritingToDiskLock + @GuardedBy("mWritingToDiskLock") private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) { long startTime = 0; long existsTime = 0; diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java index 8d81199185d2..cdeee3575756 100644 --- a/core/java/android/app/slice/SliceItem.java +++ b/core/java/android/app/slice/SliceItem.java @@ -216,7 +216,7 @@ public final class SliceItem implements Parcelable { * @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem */ public Slice getSlice() { - if (getFormat() == FORMAT_ACTION) { + if (FORMAT_ACTION.equals(getFormat())) { return ((Pair<PendingIntent, Slice>) mObj).second; } return (Slice) mObj; diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java index b7e353a4860c..52527ed67ae4 100644 --- a/core/java/android/hardware/location/ContextHubClient.java +++ b/core/java/android/hardware/location/ContextHubClient.java @@ -16,43 +16,48 @@ package android.hardware.location; import android.annotation.RequiresPermission; -import android.os.Handler; +import android.os.RemoteException; + +import dalvik.system.CloseGuard; import java.io.Closeable; +import java.util.concurrent.atomic.AtomicBoolean; /** * A class describing a client of the Context Hub Service. * - * Clients can send messages to nanoapps at a Context Hub through this object. + * Clients can send messages to nanoapps at a Context Hub through this object. The APIs supported + * by this object are thread-safe and can be used without external synchronization. * * @hide */ public class ContextHubClient implements Closeable { /* - * The ContextHubClient interface associated with this client. + * The proxy to the client interface at the service. */ - // TODO: Implement this interface and associate with ContextHubClient object - // private final IContextHubClient mClientInterface; + private final IContextHubClient mClientProxy; /* - * The listening callback associated with this client. + * The callback interface associated with this client. */ - private ContextHubClientCallback mCallback; + private final IContextHubClientCallback mCallbackInterface; /* * The Context Hub that this client is attached to. */ - private ContextHubInfo mAttachedHub; + private final ContextHubInfo mAttachedHub; - /* - * The handler to invoke mCallback. - */ - private Handler mCallbackHandler; + private final CloseGuard mCloseGuard = CloseGuard.get(); - ContextHubClient(ContextHubClientCallback callback, Handler handler, ContextHubInfo hubInfo) { - mCallback = callback; - mCallbackHandler = handler; + private final AtomicBoolean mIsClosed = new AtomicBoolean(false); + + /* package */ ContextHubClient( + IContextHubClient clientProxy, IContextHubClientCallback callback, + ContextHubInfo hubInfo) { + mClientProxy = clientProxy; + mCallbackInterface = callback; mAttachedHub = hubInfo; + mCloseGuard.open("close"); } /** @@ -71,7 +76,14 @@ public class ContextHubClient implements Closeable { * All futures messages targeted for this client are dropped at the service. */ public void close() { - throw new UnsupportedOperationException("TODO: Implement this"); + if (!mIsClosed.getAndSet(true)) { + mCloseGuard.close(); + try { + mClientProxy.close(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } /** @@ -90,6 +102,22 @@ public class ContextHubClient implements Closeable { @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) @ContextHubTransaction.Result public int sendMessageToNanoApp(NanoAppMessage message) { - throw new UnsupportedOperationException("TODO: Implement this"); + try { + return mClientProxy.sendMessageToNanoApp(message); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } finally { + super.finalize(); + } } } diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 241172784262..b31c7bcdb31e 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -456,6 +456,54 @@ public final class ContextHubManager { } /** + * Creates an interface to the ContextHubClient to send down to the service. + * + * @param callback the callback to invoke at the client process + * @param handler the handler to post callbacks for this client + * + * @return the callback interface + */ + private IContextHubClientCallback createClientCallback( + ContextHubClientCallback callback, Handler handler) { + return new IContextHubClientCallback.Stub() { + @Override + public void onMessageFromNanoApp(NanoAppMessage message) { + handler.post(() -> callback.onMessageFromNanoApp(message)); + } + + @Override + public void onHubReset() { + handler.post(() -> callback.onHubReset()); + } + + @Override + public void onNanoAppAborted(long nanoAppId, int abortCode) { + handler.post(() -> callback.onNanoAppAborted(nanoAppId, abortCode)); + } + + @Override + public void onNanoAppLoaded(long nanoAppId) { + handler.post(() -> callback.onNanoAppLoaded(nanoAppId)); + } + + @Override + public void onNanoAppUnloaded(long nanoAppId) { + handler.post(() -> callback.onNanoAppUnloaded(nanoAppId)); + } + + @Override + public void onNanoAppEnabled(long nanoAppId) { + handler.post(() -> callback.onNanoAppEnabled(nanoAppId)); + } + + @Override + public void onNanoAppDisabled(long nanoAppId) { + handler.post(() -> callback.onNanoAppDisabled(nanoAppId)); + } + }; + } + + /** * Creates and registers a client and its callback with the Context Hub Service. * * A client is registered with the Context Hub Service for a specified Context Hub. When the @@ -463,19 +511,37 @@ public final class ContextHubManager { * {@link ContextHubClient} object, and receive notifications through the provided callback. * * @param callback the notification callback to register - * @param hubInfo the hub to attach this client to - * @param handler the handler to invoke the callback, if null uses the main thread's Looper - * + * @param hubInfo the hub to attach this client to + * @param handler the handler to invoke the callback, if null uses the main thread's Looper * @return the registered client object * - * @see ContextHubClientCallback + * @throws IllegalArgumentException if hubInfo does not represent a valid hub + * @throws IllegalStateException if there were too many registered clients at the service + * @throws NullPointerException if callback or hubInfo is null * * @hide + * @see ContextHubClientCallback */ public ContextHubClient createClient( ContextHubClientCallback callback, ContextHubInfo hubInfo, @Nullable Handler handler) { - throw new UnsupportedOperationException( - "TODO: Implement this, and throw an exception on error"); + if (callback == null) { + throw new NullPointerException("Callback cannot be null"); + } + if (hubInfo == null) { + throw new NullPointerException("Hub info cannot be null"); + } + + Handler realHandler = (handler == null) ? new Handler(mMainLooper) : handler; + IContextHubClientCallback clientInterface = createClientCallback(callback, realHandler); + + IContextHubClient client; + try { + client = mService.createClient(clientInterface, hubInfo.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + return new ContextHubClient(client, clientInterface, hubInfo); } /** diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl new file mode 100644 index 000000000000..d81126a0ac54 --- /dev/null +++ b/core/java/android/hardware/location/IContextHubClient.aidl @@ -0,0 +1,31 @@ +/* + * Copyright 2017 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 android.hardware.location; + +import android.hardware.location.NanoAppMessage; + +/** + * @hide + */ +interface IContextHubClient { + + // Sends a message to a nanoapp + int sendMessageToNanoApp(in NanoAppMessage message); + + // Closes the connection with the Context Hub + void close(); +} diff --git a/core/java/android/hardware/location/IContextHubClientCallback.aidl b/core/java/android/hardware/location/IContextHubClientCallback.aidl new file mode 100644 index 000000000000..1c76bcbe18ce --- /dev/null +++ b/core/java/android/hardware/location/IContextHubClientCallback.aidl @@ -0,0 +1,49 @@ +/* + * Copyright 2017 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 android.hardware.location; + +import android.hardware.location.NanoAppMessage; + +/** + * An interface used by the Context Hub Service to invoke callbacks for lifecycle notifications of a + * Context Hub and nanoapps, as well as for nanoapp messaging. + * + * @hide + */ +oneway interface IContextHubClientCallback { + + // Callback invoked when receiving a message from a nanoapp. + void onMessageFromNanoApp(in NanoAppMessage message); + + // Callback invoked when the attached Context Hub has reset. + void onHubReset(); + + // Callback invoked when a nanoapp aborts at the attached Context Hub. + void onNanoAppAborted(long nanoAppId, int abortCode); + + // Callback invoked when a nanoapp is loaded at the attached Context Hub. + void onNanoAppLoaded(long nanoAppId); + + // Callback invoked when a nanoapp is unloaded from the attached Context Hub. + void onNanoAppUnloaded(long nanoAppId); + + // Callback invoked when a nanoapp is enabled at the attached Context Hub. + void onNanoAppEnabled(long nanoAppId); + + // Callback invoked when a nanoapp is disabled at the attached Context Hub. + void onNanoAppDisabled(long nanoAppId); +} diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl index ff8c1d07ce2c..1bb7c8fbb6e7 100644 --- a/core/java/android/hardware/location/IContextHubService.aidl +++ b/core/java/android/hardware/location/IContextHubService.aidl @@ -17,12 +17,14 @@ package android.hardware.location; // Declare any non-default types here with import statements -import android.hardware.location.ContextHubMessage; import android.hardware.location.ContextHubInfo; +import android.hardware.location.ContextHubMessage; import android.hardware.location.NanoApp; -import android.hardware.location.NanoAppInstanceInfo; import android.hardware.location.NanoAppFilter; +import android.hardware.location.NanoAppInstanceInfo; import android.hardware.location.IContextHubCallback; +import android.hardware.location.IContextHubClient; +import android.hardware.location.IContextHubClientCallback; /** * @hide @@ -52,4 +54,7 @@ interface IContextHubService { // send a message to a nanoApp int sendMessage(int hubHandle, int nanoAppHandle, in ContextHubMessage msg); + + // Creates a client to send and receive messages + IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId); } diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 13b9206b12ee..7836cd0952aa 100644 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -21,18 +21,16 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Paint.Align; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.Paint.Align; import android.graphics.Region.Op; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard.Key; import android.media.AudioManager; import android.os.Handler; import android.os.Message; -import android.os.UserHandle; -import android.provider.Settings; import android.util.AttributeSet; import android.util.TypedValue; import android.view.GestureDetector; @@ -986,6 +984,9 @@ public class KeyboardView extends View implements View.OnClickListener { private void sendAccessibilityEventForUnicodeCharacter(int eventType, int code) { if (mAccessibilityManager.isEnabled()) { + if (!mAccessibilityManager.isObservedEventType(eventType)) { + return; + } AccessibilityEvent event = AccessibilityEvent.obtain(eventType); onInitializeAccessibilityEvent(event); final String text; diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 02c7bd6459a6..062dcce502f6 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -117,8 +117,14 @@ public class Build { public static final String SERIAL = getString("no.such.thing"); /** - * Gets the hardware serial, if available. - * @return The serial if specified. + * Gets the hardware serial number, if available. + * + * <p class="note"><b>Note:</b> Root access may allow you to modify device identifiers, such as + * the hardware serial number. If you change these identifiers, you can use + * <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain + * proof of the device's original identifiers. + * + * @return The serial number if specified. */ @RequiresPermission(Manifest.permission.READ_PHONE_STATE) public static String getSerial() { diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl index 3314f604f940..1d2a40850454 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/core/java/android/os/IStatsCompanionService.aidl @@ -56,4 +56,7 @@ interface IStatsCompanionService { /** Send a broadcast to the specified pkg and class that it should getData now. */ oneway void sendBroadcast(String pkg, String cls); + + /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */ + oneway void triggerUidSnapshot(); } diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java index f9030124cc63..21694575631a 100644 --- a/core/java/android/provider/AlarmClock.java +++ b/core/java/android/provider/AlarmClock.java @@ -158,7 +158,6 @@ public final class AlarmClock { * <p> * Dismiss all currently expired timers. If there are no expired timers, then this is a no-op. * </p> - * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_DISMISS_TIMER = "android.intent.action.DISMISS_TIMER"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 729c0ff6febb..1e0948a46198 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10456,6 +10456,15 @@ public final class Settings { "storage_settings_clobber_threshold"; /** + * If set to 1, {@link Secure#LOCATION_MODE} will be set to {@link Secure#LOCATION_MODE_OFF} + * temporarily for all users. + * + * @hide + */ + public static final String LOCATION_GLOBAL_KILL_SWITCH = + "location_global_kill_switch"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index cd233b831522..df0842f7fb0d 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -105,6 +105,13 @@ public abstract class EuiccService extends Service { public static final String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; + /** + * Intent extra set for resolution requests containing a boolean indicating whether to ask the + * user to retry another confirmation code. + */ + public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = + "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED"; + /** Result code for a successful operation. */ public static final int RESULT_OK = 0; /** Result code indicating that an active SIM must be deactivated to perform the operation. */ diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index cf059104e373..3d01ec23b723 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -55,6 +55,8 @@ public class SurfaceControl { private static native void nativeScreenshot(IBinder displayToken, Surface consumer, Rect sourceCrop, int width, int height, int minLayer, int maxLayer, boolean allLayers, boolean useIdentityTransform); + private static native void nativeCaptureLayers(IBinder layerHandleToken, Surface consumer, + Rect sourceCrop, float frameScale); private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken, Rect sourceCrop, float frameScale); @@ -1177,14 +1179,22 @@ public class SurfaceControl { * Captures a layer and its children into the provided {@link Surface}. * * @param layerHandleToken The root layer to capture. + * @param consumer The {@link Surface} to capture the layer into. * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new * Rect()' or null if no cropping is desired. * @param frameScale The desired scale of the returned buffer; the raw * screen will be scaled up/down. - * - * @return Returns a GraphicBuffer that contains the layer capture. */ - public static GraphicBuffer captureLayers(IBinder layerHandleToken, Rect sourceCrop, + public static void captureLayers(IBinder layerHandleToken, Surface consumer, Rect sourceCrop, + float frameScale) { + nativeCaptureLayers(layerHandleToken, consumer, sourceCrop, frameScale); + } + + /** + * Same as {@link #captureLayers(IBinder, Surface, Rect, float)} except this + * captures to a {@link GraphicBuffer} instead of a {@link Surface}. + */ + public static GraphicBuffer captureLayersToBuffer(IBinder layerHandleToken, Rect sourceCrop, float frameScale) { return nativeCaptureLayers(layerHandleToken, sourceCrop, frameScale); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 345e30030534..0525ab164b02 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7208,7 +7208,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param text The announcement text. */ public void announceForAccessibility(CharSequence text) { - if (AccessibilityManager.getInstance(mContext).isEnabled() && mParent != null) { + if (AccessibilityManager.getInstance(mContext).isObservedEventType( + AccessibilityEvent.TYPE_ANNOUNCEMENT) && mParent != null) { AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_ANNOUNCEMENT); onInitializeAccessibilityEvent(event); @@ -10967,7 +10968,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0) { mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED; invalidate(); - if (AccessibilityManager.getInstance(mContext).isEnabled()) { + if (AccessibilityManager.getInstance(mContext).isObservedEventType( + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED)) { AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); event.setAction(action); @@ -11792,7 +11794,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private void sendViewTextTraversedAtGranularityEvent(int action, int granularity, int fromIndex, int toIndex) { - if (mParent == null) { + if (mParent == null || !AccessibilityManager.getInstance(mContext).isObservedEventType( + AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY)) { return; } AccessibilityEvent event = AccessibilityEvent.obtain( @@ -26182,7 +26185,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @Override public void run() { - if (AccessibilityManager.getInstance(mContext).isEnabled()) { + if (AccessibilityManager.getInstance(mContext).isObservedEventType( + AccessibilityEvent.TYPE_VIEW_SCROLLED)) { AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_SCROLLED); event.setScrollDeltaX(mDeltaX); diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 5adea669314c..1d19a9f5969a 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -16,6 +16,7 @@ package android.view.accessibility; +import android.annotation.IntDef; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -23,6 +24,8 @@ import android.util.Pools.SynchronizedPool; import com.android.internal.util.BitUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -709,6 +712,38 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par */ public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004; + + /** @hide */ + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + TYPE_VIEW_CLICKED, + TYPE_VIEW_LONG_CLICKED, + TYPE_VIEW_SELECTED, + TYPE_VIEW_FOCUSED, + TYPE_VIEW_TEXT_CHANGED, + TYPE_WINDOW_STATE_CHANGED, + TYPE_NOTIFICATION_STATE_CHANGED, + TYPE_VIEW_HOVER_ENTER, + TYPE_VIEW_HOVER_EXIT, + TYPE_TOUCH_EXPLORATION_GESTURE_START, + TYPE_TOUCH_EXPLORATION_GESTURE_END, + TYPE_WINDOW_CONTENT_CHANGED, + TYPE_VIEW_SCROLLED, + TYPE_VIEW_TEXT_SELECTION_CHANGED, + TYPE_ANNOUNCEMENT, + TYPE_VIEW_ACCESSIBILITY_FOCUSED, + TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, + TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY, + TYPE_GESTURE_DETECTION_START, + TYPE_GESTURE_DETECTION_END, + TYPE_TOUCH_INTERACTION_START, + TYPE_TOUCH_INTERACTION_END, + TYPE_WINDOWS_CHANGED, + TYPE_VIEW_CONTEXT_CLICKED, + TYPE_ASSIST_READING_CONTEXT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EventType {} + /** * Mask for {@link AccessibilityEvent} all types. * @@ -741,7 +776,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par private static final SynchronizedPool<AccessibilityEvent> sPool = new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE); - private int mEventType; + private @EventType int mEventType; private CharSequence mPackageName; private long mEventTime; int mMovementGranularity; @@ -833,7 +868,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * * @return The event type. */ - public int getEventType() { + public @EventType int getEventType() { return mEventType; } @@ -890,7 +925,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setEventType(int eventType) { + public void setEventType(@EventType int eventType) { enforceNotSealed(); mEventType = eventType; } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 35f6acba04dc..0375635fd708 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; @@ -187,6 +188,7 @@ public final class AccessibilityManager { * * @hide */ + @TestApi public interface AccessibilityServicesStateChangeListener { /** @@ -452,6 +454,18 @@ public final class AccessibilityManager { } /** + * Returns whether there are observers registered for this event type. If + * this method returns false you shuold not generate events of this type + * to conserve resources. + * + * @param type The event type. + * @return Whether the event is being observed. + */ + public boolean isObservedEventType(@AccessibilityEvent.EventType int type) { + return mIsEnabled && (mRelevantEventTypes & type) != 0; + } + + /** * Requests feedback interruption from all accessibility services. */ public void interrupt() { @@ -683,6 +697,7 @@ public final class AccessibilityManager { * for a callback on the process's main handler. * @hide */ + @TestApi public void addAccessibilityServicesStateChangeListener( @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) { synchronized (mLock) { @@ -698,6 +713,7 @@ public final class AccessibilityManager { * * @hide */ + @TestApi public void removeAccessibilityServicesStateChangeListener( @NonNull AccessibilityServicesStateChangeListener listener) { // Final CopyOnWriteArrayList - no lock needed. diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 2779aa2db93a..f675c355638c 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -31,6 +31,7 @@ import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; +import java.util.Locale; /** * Information for generating a widget to handle classified text. @@ -42,7 +43,7 @@ import java.util.List; * * <pre>{@code * // Called preferably outside the UiThread. - * TextClassification classification = textClassifier.classifyText(allText, 10, 25, null); + * TextClassification classification = textClassifier.classifyText(allText, 10, 25); * * // Called on the UiThread. * Button button = new Button(context); @@ -55,7 +56,7 @@ import java.util.List; * * <pre>{@code * // Called preferably outside the UiThread. - * final TextClassification classification = textClassifier.classifyText(allText, 10, 25, null); + * final TextClassification classification = textClassifier.classifyText(allText, 10, 25); * * // Called on the UiThread. * view.startActionMode(new ActionMode.Callback() { @@ -281,8 +282,8 @@ public final class TextClassification { @Override public String toString() { - return String.format("TextClassification {" - + "text=%s, entities=%s, labels=%s, intents=%s}", + return String.format(Locale.US, + "TextClassification {text=%s, entities=%s, labels=%s, intents=%s}", mText, mEntityConfidence, mLabels, mIntents); } @@ -421,7 +422,7 @@ public final class TextClassification { } /** - * Ensures that we have at we have storage for the default action. + * Ensures that we have storage for the default action. */ private void ensureDefaultActionAvailable() { if (mIntents.isEmpty()) mIntents.add(null); @@ -441,7 +442,7 @@ public final class TextClassification { } /** - * TextClassification optional input parameters. + * Optional input parameters for generating TextClassification. */ public static final class Options { diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index aeb84897b06b..5aaa5ad1eefe 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -23,6 +23,8 @@ import android.annotation.StringDef; import android.annotation.WorkerThread; import android.os.LocaleList; +import com.android.internal.util.Preconditions; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -37,8 +39,7 @@ public interface TextClassifier { /** @hide */ String DEFAULT_LOG_TAG = "TextClassifierImpl"; - /** @hide */ - String TYPE_UNKNOWN = ""; // TODO: Make this public API. + String TYPE_UNKNOWN = ""; String TYPE_OTHER = "other"; String TYPE_EMAIL = "email"; String TYPE_PHONE = "phone"; @@ -70,6 +71,8 @@ public interface TextClassifier { * * @throws IllegalArgumentException if text is null; selectionStartIndex is negative; * selectionEndIndex is greater than text.length() or not greater than selectionStartIndex + * + * @see #suggestSelection(CharSequence, int, int) */ @WorkerThread @NonNull @@ -78,13 +81,46 @@ public interface TextClassifier { @IntRange(from = 0) int selectionStartIndex, @IntRange(from = 0) int selectionEndIndex, @Nullable TextSelection.Options options) { + Utils.validateInput(text, selectionStartIndex, selectionEndIndex); return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build(); } /** + * Returns suggested text selection start and end indices, recognized entity types, and their + * associated confidence scores. The entity types are ordered from highest to lowest scoring. + * + * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls + * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method + * calls this method, a stack overflow error will happen. + * + * @param text text providing context for the selected text (which is specified + * by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex) + * @param selectionStartIndex start index of the selected part of text + * @param selectionEndIndex end index of the selected part of text + * + * @throws IllegalArgumentException if text is null; selectionStartIndex is negative; + * selectionEndIndex is greater than text.length() or not greater than selectionStartIndex + * * @see #suggestSelection(CharSequence, int, int, TextSelection.Options) */ - // TODO: Consider deprecating (b/68846316) + @WorkerThread + @NonNull + default TextSelection suggestSelection( + @NonNull CharSequence text, + @IntRange(from = 0) int selectionStartIndex, + @IntRange(from = 0) int selectionEndIndex) { + return suggestSelection(text, selectionStartIndex, selectionEndIndex, + (TextSelection.Options) null); + } + + /** + * See {@link #suggestSelection(CharSequence, int, int)} or + * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. + * + * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls + * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method + * calls this method, a stack overflow error will happen. + */ @WorkerThread @NonNull default TextSelection suggestSelection( @@ -92,7 +128,10 @@ public interface TextClassifier { @IntRange(from = 0) int selectionStartIndex, @IntRange(from = 0) int selectionEndIndex, @Nullable LocaleList defaultLocales) { - return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build(); + final TextSelection.Options options = (defaultLocales != null) + ? new TextSelection.Options().setDefaultLocales(defaultLocales) + : null; + return suggestSelection(text, selectionStartIndex, selectionEndIndex, options); } /** @@ -107,6 +146,8 @@ public interface TextClassifier { * * @throws IllegalArgumentException if text is null; startIndex is negative; * endIndex is greater than text.length() or not greater than startIndex + * + * @see #classifyText(CharSequence, int, int) */ @WorkerThread @NonNull @@ -115,13 +156,45 @@ public interface TextClassifier { @IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex, @Nullable TextClassification.Options options) { + Utils.validateInput(text, startIndex, endIndex); return TextClassification.EMPTY; } /** + * Classifies the specified text and returns a {@link TextClassification} object that can be + * used to generate a widget for handling the classified text. + * + * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls + * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method + * calls this method, a stack overflow error will happen. + * + * @param text text providing context for the text to classify (which is specified + * by the sub sequence starting at startIndex and ending at endIndex) + * @param startIndex start index of the text to classify + * @param endIndex end index of the text to classify + * + * @throws IllegalArgumentException if text is null; startIndex is negative; + * endIndex is greater than text.length() or not greater than startIndex + * * @see #classifyText(CharSequence, int, int, TextClassification.Options) */ - // TODO: Consider deprecating (b/68846316) + @WorkerThread + @NonNull + default TextClassification classifyText( + @NonNull CharSequence text, + @IntRange(from = 0) int startIndex, + @IntRange(from = 0) int endIndex) { + return classifyText(text, startIndex, endIndex, (TextClassification.Options) null); + } + + /** + * See {@link #classifyText(CharSequence, int, int, TextClassification.Options)} or + * {@link #classifyText(CharSequence, int, int)}. + * + * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls + * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method + * calls this method, a stack overflow error will happen. + */ @WorkerThread @NonNull default TextClassification classifyText( @@ -129,7 +202,10 @@ public interface TextClassifier { @IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex, @Nullable LocaleList defaultLocales) { - return TextClassification.EMPTY; + final TextClassification.Options options = (defaultLocales != null) + ? new TextClassification.Options().setDefaultLocales(defaultLocales) + : null; + return classifyText(text, startIndex, endIndex, options); } /** @@ -137,17 +213,39 @@ public interface TextClassifier { * information. * * @param text the text to generate annotations for - * @param options configuration for link generation. If null, defaults will be used. + * @param options configuration for link generation * * @throws IllegalArgumentException if text is null + * + * @see #generateLinks(CharSequence) */ @WorkerThread default TextLinks generateLinks( @NonNull CharSequence text, @Nullable TextLinks.Options options) { + Utils.validateInput(text); return new TextLinks.Builder(text.toString()).build(); } /** + * Returns a {@link TextLinks} that may be applied to the text to annotate it with links + * information. + * + * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls + * {@link #generateLinks(CharSequence, TextLinks.Options)}. If that method calls this method, + * a stack overflow error will happen. + * + * @param text the text to generate annotations for + * + * @throws IllegalArgumentException if text is null + * + * @see #generateLinks(CharSequence, TextLinks.Options) + */ + @WorkerThread + default TextLinks generateLinks(@NonNull CharSequence text) { + return generateLinks(text, null); + } + + /** * Logs a TextClassifier event. * * @param source the text classifier used to generate this event @@ -164,4 +262,38 @@ public interface TextClassifier { default TextClassifierConstants getSettings() { return TextClassifierConstants.DEFAULT; } + + + /** + * Utility functions for TextClassifier methods. + * + * <ul> + * <li>Provides validation of input parameters to TextClassifier methods + * </ul> + * + * Intended to be used only in this package. + * @hide + */ + final class Utils { + + /** + * @throws IllegalArgumentException if text is null; startIndex is negative; + * endIndex is greater than text.length() or is not greater than startIndex; + * options is null + */ + static void validateInput( + @NonNull CharSequence text, int startIndex, int endIndex) { + Preconditions.checkArgument(text != null); + Preconditions.checkArgument(startIndex >= 0); + Preconditions.checkArgument(endIndex <= text.length()); + Preconditions.checkArgument(endIndex > startIndex); + } + + /** + * @throws IllegalArgumentException if text is null or options is null + */ + static void validateInput(@NonNull CharSequence text) { + Preconditions.checkArgument(text != null); + } + } } diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 2ad6e02c4849..df5e35f062b7 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -90,8 +90,8 @@ final class TextClassifierImpl implements TextClassifier { @Override public TextSelection suggestSelection( @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex, - @Nullable TextSelection.Options options) { - validateInput(text, selectionStartIndex, selectionEndIndex); + @NonNull TextSelection.Options options) { + Utils.validateInput(text, selectionStartIndex, selectionEndIndex); try { if (text.length() > 0) { final LocaleList locales = (options == null) ? null : options.getDefaultLocales(); @@ -141,18 +141,10 @@ final class TextClassifierImpl implements TextClassifier { } @Override - public TextSelection suggestSelection( - @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex, - @Nullable LocaleList defaultLocales) { - return suggestSelection(text, selectionStartIndex, selectionEndIndex, - new TextSelection.Options().setDefaultLocales(defaultLocales)); - } - - @Override public TextClassification classifyText( @NonNull CharSequence text, int startIndex, int endIndex, - @Nullable TextClassification.Options options) { - validateInput(text, startIndex, endIndex); + @NonNull TextClassification.Options options) { + Utils.validateInput(text, startIndex, endIndex); try { if (text.length() > 0) { final String string = text.toString(); @@ -176,17 +168,9 @@ final class TextClassifierImpl implements TextClassifier { } @Override - public TextClassification classifyText( - @NonNull CharSequence text, int startIndex, int endIndex, - @Nullable LocaleList defaultLocales) { - return classifyText(text, startIndex, endIndex, - new TextClassification.Options().setDefaultLocales(defaultLocales)); - } - - @Override public TextLinks generateLinks( - @NonNull CharSequence text, @Nullable TextLinks.Options options) { - Preconditions.checkNotNull(text); + @NonNull CharSequence text, @NonNull TextLinks.Options options) { + Utils.validateInput(text); final String textString = text.toString(); final TextLinks.Builder builder = new TextLinks.Builder(textString); try { @@ -486,17 +470,6 @@ final class TextClassifierImpl implements TextClassifier { } /** - * @throws IllegalArgumentException if text is null; startIndex is negative; - * endIndex is greater than text.length() or is not greater than startIndex - */ - private static void validateInput(@NonNull CharSequence text, int startIndex, int endIndex) { - Preconditions.checkArgument(text != null); - Preconditions.checkArgument(startIndex >= 0); - Preconditions.checkArgument(endIndex <= text.length()); - Preconditions.checkArgument(endIndex > startIndex); - } - - /** * Creates intents based on the classification type. */ private static final class IntentFactory { diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index f3cc827f8889..76748d2b191a 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -161,39 +161,28 @@ public final class TextLinks { * Optional input parameters for generating TextLinks. */ public static final class Options { - private final LocaleList mLocaleList; - private Options(LocaleList localeList) { - this.mLocaleList = localeList; - } + private LocaleList mDefaultLocales; /** - * Builder to construct Options. + * @param defaultLocales ordered list of locale preferences that may be used to disambiguate + * the provided text. If no locale preferences exist, set this to null or an empty + * locale list. */ - public static final class Builder { - private LocaleList mLocaleList; - - /** - * Sets the LocaleList to use. - * - * @return this Builder. - */ - public Builder setLocaleList(@Nullable LocaleList localeList) { - this.mLocaleList = localeList; - return this; - } - - /** - * Builds the Options object. - */ - public Options build() { - return new Options(mLocaleList); - } + public Options setDefaultLocales(@Nullable LocaleList defaultLocales) { + mDefaultLocales = defaultLocales; + return this; } - public @Nullable LocaleList getDefaultLocales() { - return mLocaleList; + + /** + * @return ordered list of locale preferences that can be used to disambiguate + * the provided text. + */ + @Nullable + public LocaleList getDefaultLocales() { + return mDefaultLocales; } - }; + } /** * A function to create spans from TextLinks. @@ -204,13 +193,10 @@ public final class TextLinks { * @hide */ public static final Function<TextLink, ClickableSpan> DEFAULT_SPAN_FACTORY = - new Function<TextLink, ClickableSpan>() { - @Override - public ClickableSpan apply(TextLink textLink) { - // TODO: Implement. - throw new UnsupportedOperationException("Not yet implemented"); - } - }; + textLink -> { + // TODO: Implement. + throw new UnsupportedOperationException("Not yet implemented"); + }; /** * A builder to construct a TextLinks instance. diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 0a67954a2e33..480b27a73fc1 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -26,6 +26,7 @@ import android.view.textclassifier.TextClassifier.EntityType; import com.android.internal.util.Preconditions; import java.util.List; +import java.util.Locale; /** * Information about where text selection should be. @@ -114,8 +115,8 @@ public final class TextSelection { @Override public String toString() { - return String.format("TextSelection {%d, %d, %s}", - mStartIndex, mEndIndex, mEntityConfidence); + return String.format(Locale.US, + "TextSelection {%d, %d, %s}", mStartIndex, mEndIndex, mEntityConfidence); } /** @@ -185,7 +186,7 @@ public final class TextSelection { } /** - * TextSelection optional input parameters. + * Optional input parameters for generating TextSelection. */ public static final class Options { diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 517ad07c8323..46c39834060b 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -364,13 +364,13 @@ public class WebViewClient { } /** - * Notify the host application to handle a SSL client certificate - * request. The host application is responsible for showing the UI - * if desired and providing the keys. There are three ways to - * respond: proceed(), cancel() or ignore(). Webview stores the response - * in memory (for the life of the application) if proceed() or cancel() is - * called and does not call {@code onReceivedClientCertRequest()} again for the - * same host and port pair. Webview does not store the response if ignore() + * Notify the host application to handle a SSL client certificate request. The host application + * is responsible for showing the UI if desired and providing the keys. There are three ways to + * respond: {@link ClientCertRequest#proceed}, {@link ClientCertRequest#cancel}, or {@link + * ClientCertRequest#ignore}. Webview stores the response in memory (for the life of the + * application) if {@link ClientCertRequest#proceed} or {@link ClientCertRequest#cancel} is + * called and does not call {@code onReceivedClientCertRequest()} again for the same host and + * port pair. Webview does not store the response if {@link ClientCertRequest#ignore} * is called. Note that, multiple layers in chromium network stack might be * caching the responses, so the behavior for ignore is only a best case * effort. diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 4d3189efa64e..b37928066899 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1952,7 +1952,8 @@ public class NumberPicker extends LinearLayout { CharSequence beforeText = mInputText.getText(); if (!text.equals(beforeText.toString())) { mInputText.setText(text); - if (AccessibilityManager.getInstance(mContext).isEnabled()) { + if (AccessibilityManager.getInstance(mContext).isObservedEventType( + AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) { AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); mInputText.onInitializeAccessibilityEvent(event); @@ -2612,7 +2613,7 @@ public class NumberPicker extends LinearLayout { } private void sendAccessibilityEventForVirtualText(int eventType) { - if (AccessibilityManager.getInstance(mContext).isEnabled()) { + if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) { AccessibilityEvent event = AccessibilityEvent.obtain(eventType); mInputText.onInitializeAccessibilityEvent(event); mInputText.onPopulateAccessibilityEvent(event); @@ -2623,7 +2624,7 @@ public class NumberPicker extends LinearLayout { private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType, String text) { - if (AccessibilityManager.getInstance(mContext).isEnabled()) { + if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) { AccessibilityEvent event = AccessibilityEvent.obtain(eventType); event.setClassName(Button.class.getName()); event.setPackageName(mContext.getPackageName()); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index d9bc51fffd6a..71532a72d7b4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10836,6 +10836,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex, int removedCount, int addedCount) { + if (!AccessibilityManager.getInstance(mContext).isObservedEventType( + AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) { + return; + } AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); event.setFromIndex(fromIndex); diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index d80712006a53..bfde6ac38e55 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -504,7 +504,8 @@ public class Toast { private void trySendAccessibilityEvent() { AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mView.getContext()); - if (!accessibilityManager.isEnabled()) { + if (!accessibilityManager.isObservedEventType( + AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) { return; } // treat toasts as notifications since they are used to diff --git a/core/java/com/android/internal/util/RingBuffer.java b/core/java/com/android/internal/util/RingBuffer.java index c22be2ccf076..9a6e542ccbbd 100644 --- a/core/java/com/android/internal/util/RingBuffer.java +++ b/core/java/com/android/internal/util/RingBuffer.java @@ -60,6 +60,25 @@ public class RingBuffer<T> { mBuffer[indexOf(mCursor++)] = t; } + /** + * Returns object of type <T> at the next writable slot, creating one if it is not already + * available. In case of any errors while creating the object, <code>null</code> will + * be returned. + */ + public T getNextSlot() { + final int nextSlotIdx = indexOf(mCursor++); + T item = mBuffer[nextSlotIdx]; + if (item == null) { + try { + item = (T) mBuffer.getClass().getComponentType().newInstance(); + } catch (IllegalAccessException | InstantiationException e) { + return null; + } + mBuffer[nextSlotIdx] = item; + } + return item; + } + public T[] toArray() { // Only generic way to create a T[] from another T[] T[] out = Arrays.copyOf(mBuffer, size(), (Class<T[]>) mBuffer.getClass()); diff --git a/core/java/com/android/internal/view/TooltipPopup.java b/core/java/com/android/internal/view/TooltipPopup.java index d38ea2c19af4..24f0b0cc91c5 100644 --- a/core/java/com/android/internal/view/TooltipPopup.java +++ b/core/java/com/android/internal/view/TooltipPopup.java @@ -142,7 +142,7 @@ public class TooltipPopup { mTmpAnchorPos[1] -= mTmpAppPos[1]; // mTmpAnchorPos is now relative to the main app window. - outParams.x = mTmpAnchorPos[0] + offsetX - mTmpDisplayFrame.width() / 2; + outParams.x = mTmpAnchorPos[0] + offsetX - appView.getWidth() / 2; final int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); mContentView.measure(spec, spec); @@ -157,6 +157,9 @@ public class TooltipPopup { outParams.y = yBelow; } } else { + // Use mTmpDisplayFrame.height() as the lower boundary instead of appView.getHeight(), + // as the latter includes the navigation bar, and tooltips do not look good over + // the navigation bar. if (yBelow + tooltipHeight <= mTmpDisplayFrame.height()) { outParams.y = yBelow; } else { diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java index 50ad547e6e65..759a41a2c0ca 100644 --- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java +++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java @@ -186,6 +186,9 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { } final AccessibilityEvent event = createEvent(virtualViewId, eventType); + if (event == null) { + return false; + } return parent.requestSendAccessibilityEvent(mView, event); } @@ -240,6 +243,9 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { if (parent != null) { final AccessibilityEvent event = createEvent(virtualViewId, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + if (event == null) { + return; + } event.setContentChangeTypes(changeTypes); parent.requestSendAccessibilityEvent(mView, event); } @@ -305,6 +311,9 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { * the specified item. */ private AccessibilityEvent createEventForHost(int eventType) { + if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) { + return null; + } final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); mView.onInitializeAccessibilityEvent(event); @@ -325,6 +334,9 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { * the specified item. */ private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) { + if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) { + return null; + } final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); event.setEnabled(true); event.setClassName(DEFAULT_CLASS_NAME); diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index d7300c4947d9..601bd989ce42 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -25,6 +25,7 @@ #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/JNIHelp.h> #include "core_jni_helpers.h" +#include "scoped_nullable_primitive_array.h" #include <cstdint> #include <vector> #include <list> @@ -87,9 +88,8 @@ static void nFinish(jlong nativePtr) { static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, jfloatArray recycleWidths, jfloatArray recycleAscents, jfloatArray recycleDescents, jintArray recycleFlags, - jint recycleLength, size_t nBreaks, const jint* breaks, - const jfloat* widths, const jfloat* ascents, const jfloat* descents, - const jint* flags) { + jint recycleLength, const minikin::LineBreakResult& result) { + const size_t nBreaks = result.breakPoints.size(); if ((size_t)recycleLength < nBreaks) { // have to reallocate buffers recycleBreaks = env->NewIntArray(nBreaks); @@ -105,11 +105,11 @@ static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags); } // copy data - env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, breaks); - env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, widths); - env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, ascents); - env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, descents); - env->SetIntArrayRegion(recycleFlags, 0, nBreaks, flags); + env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data()); + env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data()); + env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data()); + env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data()); + env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data()); } static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, @@ -136,34 +136,22 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, minikin::android::StaticLayoutNative* builder = toNative(nativePtr); ScopedCharArrayRO text(env, javaText); + ScopedNullableIntArrayRO tabStops(env, variableTabStops); - // TODO: Reorganize minikin APIs. - minikin::LineBreaker b(minikin::U16StringPiece(text.get(), length)); - if (variableTabStops == nullptr) { - b.setTabStops(nullptr, 0, defaultTabStop); - } else { - ScopedIntArrayRO stops(env, variableTabStops); - b.setTabStops(stops.get(), stops.size(), defaultTabStop); - } - b.setStrategy(builder->getStrategy()); - b.setHyphenationFrequency(builder->getFrequency()); - b.setJustified(builder->isJustified()); - b.setLineWidthDelegate(builder->buildLineWidthDelegate( - firstWidth, firstWidthLineCount, restWidth, indentsOffset)); - - builder->addRuns(&b); - - size_t nBreaks = b.computeBreaks(); + minikin::U16StringPiece u16Text(text.get(), length); + minikin::MeasuredText measuredText = builder->measureText(u16Text); + minikin::LineBreakResult result = builder->computeBreaks( + u16Text, measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, + tabStops.get(), tabStops.size(), defaultTabStop); recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents, - recycleFlags, recycleLength, nBreaks, b.getBreaks(), b.getWidths(), b.getAscents(), - b.getDescents(), b.getFlags()); + recycleFlags, recycleLength, result); - env->SetFloatArrayRegion(charWidths, 0, b.size(), b.charWidths()); + env->SetFloatArrayRegion(charWidths, 0, measuredText.widths.size(), measuredText.widths.data()); builder->clearRuns(); - return static_cast<jint>(nBreaks); + return static_cast<jint>(result.breakPoints.size()); } // Basically similar to Paint.getTextRunAdvances but with C++ interface diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 421e0de52cc0..3ad4da6b6580 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -517,7 +517,23 @@ static jint nativeAttachAndQueueBuffer(JNIEnv *env, jclass clazz, jlong nativeOb jobject graphicBuffer) { Surface* surface = reinterpret_cast<Surface*>(nativeObject); sp<GraphicBuffer> bp = graphicBufferForJavaObject(env, graphicBuffer); - int err = Surface::attachAndQueueBuffer(surface, bp); + if (bp == nullptr) { + return BAD_VALUE; + } + int err = ((ANativeWindow*)surface)->perform(surface, NATIVE_WINDOW_API_CONNECT, + NATIVE_WINDOW_API_CPU); + if (err != OK) { + return err; + } + err = surface->attachBuffer(bp->getNativeBuffer()); + if (err != OK) { + return err; + } + err = ((ANativeWindow*)surface)->queueBuffer(surface, bp->getNativeBuffer(), -1); + if (err != OK) { + return err; + } + err = surface->disconnect(NATIVE_WINDOW_API_CPU); return err; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 8c968a2a7083..f77e6c4fa7b8 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -167,7 +167,7 @@ static jobject nativeScreenshotToBuffer(JNIEnv* env, jclass clazz, maxLayer = INT32_MAX; } sp<GraphicBuffer> buffer; - status_t res = ScreenshotClient::capture(displayToken, + status_t res = ScreenshotClient::captureToBuffer(displayToken, sourceCrop, width, height, minLayer, maxLayer, useIdentityTransform, rotation, &buffer); if (res != NO_ERROR) { @@ -201,18 +201,15 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, maxLayer = INT32_MAX; } - sp<GraphicBuffer> buffer; - res = ScreenshotClient::capture(displayToken, sourceCrop, width, height, - minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation), &buffer); + res = screenshot->update(displayToken, sourceCrop, width, height, + minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation)); if (res != NO_ERROR) { return NULL; } SkColorType colorType; SkAlphaType alphaType; - - PixelFormat format = buffer->getPixelFormat(); - switch (format) { + switch (screenshot->getFormat()) { case PIXEL_FORMAT_RGBX_8888: { colorType = kRGBA_8888_SkColorType; alphaType = kOpaque_SkAlphaType; @@ -238,20 +235,66 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, } } - SkImageInfo info = SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(), - colorType, alphaType, - SkColorSpace::MakeSRGB()); + sk_sp<SkColorSpace> colorSpace; + if (screenshot->getDataSpace() == HAL_DATASPACE_DISPLAY_P3) { + colorSpace = SkColorSpace::MakeRGB( + SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut); + } else { + colorSpace = SkColorSpace::MakeSRGB(); + } + + SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(), + screenshot->getHeight(), + colorType, + alphaType, + colorSpace); - auto bitmap = sk_sp<Bitmap>(new Bitmap(buffer.get(), info)); - return bitmap::createBitmap(env, bitmap.release(), - android::bitmap::kBitmapCreateFlag_Premultiplied, NULL); + const size_t rowBytes = + screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat()); + + if (!screenshotInfo.width() || !screenshotInfo.height()) { + return NULL; + } + + auto bitmap = new Bitmap( + (void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot, + screenshotInfo, rowBytes); + screenshot.release(); + bitmap->setImmutable(); + return bitmap::createBitmap(env, bitmap, + android::bitmap::kBitmapCreateFlag_Premultiplied, NULL); } static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, jobject surfaceObj, jobject sourceCropObj, jint width, jint height, jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) { sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); - if (displayToken == NULL) { + if (displayToken != NULL) { + sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj); + if (consumer != NULL) { + int left = env->GetIntField(sourceCropObj, gRectClassInfo.left); + int top = env->GetIntField(sourceCropObj, gRectClassInfo.top); + int right = env->GetIntField(sourceCropObj, gRectClassInfo.right); + int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom); + Rect sourceCrop(left, top, right, bottom); + + if (allLayers) { + minLayer = INT32_MIN; + maxLayer = INT32_MAX; + } + ScreenshotClient::capture(displayToken, + consumer->getIGraphicBufferProducer(), sourceCrop, + width, height, minLayer, maxLayer, + useIdentityTransform); + } + } +} + +static void nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken, + jobject surfaceObj, jobject sourceCropObj, jfloat frameScale) { + + sp<IBinder> layerHandle = ibinderForJavaObject(env, layerHandleToken); + if (layerHandle == NULL) { return; } @@ -265,19 +308,11 @@ static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, sourceCrop = rectFromObj(env, sourceCropObj); } - if (allLayers) { - minLayer = INT32_MIN; - maxLayer = INT32_MAX; - } - - sp<GraphicBuffer> buffer; - ScreenshotClient::capture(displayToken, sourceCrop, width, height, minLayer, maxLayer, - useIdentityTransform, 0, &buffer); - - Surface::attachAndQueueBuffer(consumer.get(), buffer); + ScreenshotClient::captureLayers(layerHandle, consumer->getIGraphicBufferProducer(), sourceCrop, + frameScale); } -static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken, +static jobject nativeCaptureLayersToBuffer(JNIEnv* env, jclass clazz, jobject layerHandleToken, jobject sourceCropObj, jfloat frameScale) { sp<IBinder> layerHandle = ibinderForJavaObject(env, layerHandleToken); @@ -291,7 +326,8 @@ static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandl } sp<GraphicBuffer> buffer; - status_t res = ScreenshotClient::captureLayers(layerHandle, sourceCrop, frameScale, &buffer); + status_t res = ScreenshotClient::captureLayersToBuffer(layerHandle, sourceCrop, frameScale, + &buffer); if (res != NO_ERROR) { return NULL; } @@ -974,8 +1010,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { {"nativeScreenshotToBuffer", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/GraphicBuffer;", (void*)nativeScreenshotToBuffer }, - {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;", + {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;F)V", (void*)nativeCaptureLayers }, + {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;", + (void*)nativeCaptureLayersToBuffer }, }; int register_android_view_SurfaceControl(JNIEnv* env) diff --git a/core/jni/scoped_nullable_primitive_array.h b/core/jni/scoped_nullable_primitive_array.h new file mode 100644 index 000000000000..77f4c9d14f07 --- /dev/null +++ b/core/jni/scoped_nullable_primitive_array.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCOPED_NULLABLE_PRIMITIVE_ARRAY_H +#define SCOPED_NULLABLE_PRIMITIVE_ARRAY_H + +#include <jni.h> + +namespace android { + +#define ARRAY_TRAITS(ARRAY_TYPE, POINTER_TYPE, NAME) \ +class NAME ## ArrayTraits { \ +public: \ + static constexpr void getArrayRegion(JNIEnv* env, ARRAY_TYPE array, size_t start, \ + size_t len, POINTER_TYPE out) { \ + env->Get ## NAME ## ArrayRegion(array, start, len, out); \ + } \ + \ + static constexpr POINTER_TYPE getArrayElements(JNIEnv* env, ARRAY_TYPE array) { \ + return env->Get ## NAME ## ArrayElements(array, nullptr); \ + } \ + \ + static constexpr void releaseArrayElements(JNIEnv* env, ARRAY_TYPE array, \ + POINTER_TYPE buffer, jint mode) { \ + env->Release ## NAME ## ArrayElements(array, buffer, mode); \ + } \ +}; \ + +ARRAY_TRAITS(jbooleanArray, jboolean*, Boolean) +ARRAY_TRAITS(jbyteArray, jbyte*, Byte) +ARRAY_TRAITS(jcharArray, jchar*, Char) +ARRAY_TRAITS(jdoubleArray, jdouble*, Double) +ARRAY_TRAITS(jfloatArray, jfloat*, Float) +ARRAY_TRAITS(jintArray, jint*, Int) +ARRAY_TRAITS(jlongArray, jlong*, Long) +ARRAY_TRAITS(jshortArray, jshort*, Short) + +#undef ARRAY_TRAITS + +template<typename JavaArrayType, typename PrimitiveType, class Traits, size_t preallocSize = 10> +class ScopedArrayRO { +public: + ScopedArrayRO(JNIEnv* env, JavaArrayType javaArray) : mEnv(env), mJavaArray(javaArray) { + if (mJavaArray == nullptr) { + mSize = 0; + mRawArray = nullptr; + } else { + mSize = mEnv->GetArrayLength(mJavaArray); + if (mSize <= preallocSize) { + Traits::getArrayRegion(mEnv, mJavaArray, 0, mSize, mBuffer); + mRawArray = mBuffer; + } else { + mRawArray = Traits::getArrayElements(mEnv, mJavaArray); + } + } + } + + ~ScopedArrayRO() { + if (mRawArray != nullptr && mRawArray != mBuffer) { + Traits::releaseArrayElements(mEnv, mJavaArray, mRawArray, JNI_ABORT); + } + } + + const PrimitiveType* get() const { return mRawArray; } + const PrimitiveType& operator[](size_t n) const { return mRawArray[n]; } + size_t size() const { return mSize; } + +private: + JNIEnv* const mEnv; + JavaArrayType mJavaArray; + PrimitiveType* mRawArray; + size_t mSize; + PrimitiveType mBuffer[preallocSize]; + DISALLOW_COPY_AND_ASSIGN(ScopedArrayRO); +}; + +// ScopedNullable***ArrayRO provide convenient read-only access to Java array from JNI code. +// These accept nullptr. In that case, get() returns nullptr and size() returns 0. +using ScopedNullableBooleanArrayRO = ScopedArrayRO<jbooleanArray, jboolean, BooleanArrayTraits>; +using ScopedNullableByteArrayRO = ScopedArrayRO<jbyteArray, jbyte, ByteArrayTraits>; +using ScopedNullableCharArrayRO = ScopedArrayRO<jcharArray, jchar, CharArrayTraits>; +using ScopedNullableDoubleArrayRO = ScopedArrayRO<jdoubleArray, jdouble, DoubleArrayTraits>; +using ScopedNullableFloatArrayRO = ScopedArrayRO<jfloatArray, jfloat, FloatArrayTraits>; +using ScopedNullableIntArrayRO = ScopedArrayRO<jintArray, jint, IntArrayTraits>; +using ScopedNullableLongArrayRO = ScopedArrayRO<jlongArray, jlong, LongArrayTraits>; +using ScopedNullableShortArrayRO = ScopedArrayRO<jshortArray, jshort, ShortArrayTraits>; + +} // namespace android + +#endif // SCOPED_NULLABLE_PRIMITIVE_ARRAY_H diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 171f74f9ead8..5f756fabdb78 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3169,6 +3169,10 @@ classes are instantiated in the order of the array. --> <string-array translatable="false" name="config_deviceSpecificSystemServices"></string-array> + <!-- Class name of the device specific implementation to replace the DevicePolicyManagerService + or empty if the default should be used. --> + <string translatable="false" name="config_deviceSpecificDevicePolicyManagerService"></string> + <!-- Component name of media projection permission dialog --> <string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a1158162ee73..ad947b7caad3 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -459,6 +459,7 @@ <java-symbol type="bool" name="config_hasPermanentDpad" /> <java-symbol type="bool" name="config_useDefaultFocusHighlight" /> <java-symbol type="array" name="config_deviceSpecificSystemServices" /> + <java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" /> <java-symbol type="color" name="tab_indicator_text_v4" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index d63d00c86239..39310a84a2f7 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1125,6 +1125,7 @@ easier. <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item> <!-- Color palette --> + <item name="colorBackground">@color/background_device_default_light</item> <item name="colorPrimary">@color/primary_device_default_settings_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item> <item name="colorSecondary">@color/secondary_device_default_settings_light</item> diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java index 5f6f62a67b05..978ea7aa9c00 100644 --- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java +++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java @@ -45,8 +45,9 @@ import java.util.concurrent.TimeUnit; @LargeTest @RunWith(AndroidJUnit4.class) abstract class AbstractCrossUserContentResolverTest { - private final static int TIMEOUT_SERVICE_CONNECTION_SEC = 4; - private final static int TIMEOUT_CONTENT_CHANGE_SEC = 4; + private static final int TIMEOUT_SERVICE_CONNECTION_SEC = 4; + private static final int TIMEOUT_CONTENT_CHANGE_SEC = 4; + private static final int TIMEOUT_USER_UNLOCK_SEC = 4; private Context mContext; protected UserManager mUm; @@ -61,7 +62,7 @@ abstract class AbstractCrossUserContentResolverTest { mCrossUserId = userInfo.id; final PackageManager pm = mContext.getPackageManager(); pm.installExistingPackageAsUser(mContext.getPackageName(), mCrossUserId); - ActivityManager.getService().startUserInBackground(mCrossUserId); + unlockUser(); final CountDownLatch connectionLatch = new CountDownLatch(1); mServiceConnection = new CrossUserContentServiceConnection(connectionLatch); @@ -77,6 +78,30 @@ abstract class AbstractCrossUserContentResolverTest { protected abstract UserInfo createUser() throws RemoteException ; + private void unlockUser() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) + == mCrossUserId) { + latch.countDown(); + } + } + }; + mContext.registerReceiverAsUser(receiver, UserHandle.of(mCrossUserId), + new IntentFilter(Intent.ACTION_USER_UNLOCKED), null, null); + ActivityManager.getService().startUserInBackground(mCrossUserId); + + try { + if (!latch.await(TIMEOUT_USER_UNLOCK_SEC, TimeUnit.SECONDS)) { + fail("Timed out waiting for the u" + mCrossUserId + " to unlock"); + } + } finally { + mContext.unregisterReceiver(receiver); + } + } + @After public void tearDown() throws Exception { if (mCrossUserId != -1) { diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 454d2096eca8..4ce60294e615 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -231,6 +231,7 @@ public class SettingsBackupTest { Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, + Settings.Global.LOCATION_GLOBAL_KILL_SWITCH, Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, Settings.Global.LOCK_SOUND, Settings.Global.LOW_BATTERY_SOUND, diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index 41686fa135e0..9092c852db47 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -45,6 +45,9 @@ public class TextClassificationManagerTest { private TextClassificationManager mTcm; private TextClassifier mClassifier; + private TextSelection.Options mSelectionOptions; + private TextClassification.Options mClassificationOptions; + private TextLinks.Options mLinksOptions; @Before public void setup() { @@ -52,6 +55,9 @@ public class TextClassificationManagerTest { .getSystemService(TextClassificationManager.class); mTcm.setTextClassifier(null); mClassifier = mTcm.getTextClassifier(); + mSelectionOptions = new TextSelection.Options().setDefaultLocales(LOCALES); + mClassificationOptions = new TextClassification.Options().setDefaultLocales(LOCALES); + mLinksOptions = new TextLinks.Options().setDefaultLocales(LOCALES); } @Test @@ -66,24 +72,9 @@ public class TextClassificationManagerTest { int smartStartIndex = text.indexOf(suggested); int smartEndIndex = smartStartIndex + suggested.length(); - assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), - isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL)); - } - - @Test - public void testSmartSelection_nullLocaleList() { - if (isTextClassifierDisabled()) return; - - String text = "Contact me at droid@android.com"; - String selected = "droid"; - String suggested = "droid@android.com"; - int startIndex = text.indexOf(selected); - int endIndex = startIndex + selected.length(); - int smartStartIndex = text.indexOf(suggested); - int smartEndIndex = smartStartIndex + suggested.length(); - LocaleList nullLocales = null; - - assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, nullLocales), + TextSelection selection = mClassifier.suggestSelection( + text, startIndex, endIndex, mSelectionOptions); + assertThat(selection, isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL)); } @@ -99,7 +90,9 @@ public class TextClassificationManagerTest { int smartStartIndex = text.indexOf(suggested); int smartEndIndex = smartStartIndex + suggested.length(); - assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), + TextSelection selection = mClassifier.suggestSelection( + text, startIndex, endIndex, mSelectionOptions); + assertThat(selection, isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL)); } @@ -112,7 +105,9 @@ public class TextClassificationManagerTest { int startIndex = text.indexOf(selected); int endIndex = startIndex + selected.length(); - assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES), + TextSelection selection = mClassifier.suggestSelection( + text, startIndex, endIndex, mSelectionOptions); + assertThat(selection, isTextSelection(startIndex, endIndex, NO_TYPE)); } @@ -124,7 +119,10 @@ public class TextClassificationManagerTest { String classifiedText = "droid@android.com"; int startIndex = text.indexOf(classifiedText); int endIndex = startIndex + classifiedText.length(); - assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES), + + TextClassification classification = mClassifier.classifyText( + text, startIndex, endIndex, mClassificationOptions); + assertThat(classification, isTextClassification( classifiedText, TextClassifier.TYPE_EMAIL, @@ -139,7 +137,10 @@ public class TextClassificationManagerTest { String classifiedText = "www.android.com"; int startIndex = text.indexOf(classifiedText); int endIndex = startIndex + classifiedText.length(); - assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES), + + TextClassification classification = mClassifier.classifyText( + text, startIndex, endIndex, mClassificationOptions); + assertThat(classification, isTextClassification( classifiedText, TextClassifier.TYPE_URL, @@ -154,7 +155,10 @@ public class TextClassificationManagerTest { String classifiedText = "HTTP://ANDROID.COM"; int startIndex = text.indexOf(classifiedText); int endIndex = startIndex + classifiedText.length(); - assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES), + + TextClassification classification = mClassifier.classifyText( + text, startIndex, endIndex, mClassificationOptions); + assertThat(classification, isTextClassification( classifiedText, TextClassifier.TYPE_URL, @@ -162,22 +166,6 @@ public class TextClassificationManagerTest { } @Test - public void testTextClassifyText_nullLocaleList() { - if (isTextClassifierDisabled()) return; - - String text = "Contact me at droid@android.com"; - String classifiedText = "droid@android.com"; - int startIndex = text.indexOf(classifiedText); - int endIndex = startIndex + classifiedText.length(); - LocaleList nullLocales = null; - assertThat(mClassifier.classifyText(text, startIndex, endIndex, nullLocales), - isTextClassification( - classifiedText, - TextClassifier.TYPE_EMAIL, - "mailto:" + classifiedText)); - } - - @Test public void testGenerateLinks() { if (isTextClassifierDisabled()) return; @@ -210,13 +198,14 @@ public class TextClassificationManagerTest { int startIndex = text.indexOf(classifiedText); int endIndex = startIndex + classifiedText.length(); - Collection<TextLinks.TextLink> links = mClassifier.generateLinks(text, null).getLinks(); + Collection<TextLinks.TextLink> links = mClassifier.generateLinks(text, mLinksOptions) + .getLinks(); for (TextLinks.TextLink link : links) { if (text.subSequence(link.getStart(), link.getEnd()).equals(classifiedText)) { assertEquals(type, link.getEntity(0)); assertEquals(startIndex, link.getStart()); assertEquals(endIndex, link.getEnd()); - assertTrue(link.getConfidenceScore(type) > .9); + assertTrue(link.getConfidenceScore(type) > 0); return; } } diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java index 682a0021b274..4b197e485ef9 100644 --- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java @@ -104,7 +104,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes() throws Exception { - assumeTrue(sCpuFreqTimesAvailable); + if (!sCpuFreqTimesAvailable) { + return; + } batteryOnScreenOn(); forceStop(); @@ -126,7 +128,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_screenOff() throws Exception { - assumeTrue(sCpuFreqTimesAvailable); + if (!sCpuFreqTimesAvailable) { + return; + } batteryOnScreenOff(); forceStop(); @@ -154,7 +158,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_isolatedProcess() throws Exception { - assumeTrue(sCpuFreqTimesAvailable); + if (!sCpuFreqTimesAvailable) { + return; + } batteryOnScreenOn(); forceStop(); diff --git a/core/tests/overlaytests/OverlayAppFiltered/Android.mk b/core/tests/overlaytests/OverlayAppFiltered/Android.mk index 8ba21df189f0..f76de7a93b2e 100644 --- a/core/tests/overlaytests/OverlayAppFiltered/Android.mk +++ b/core/tests/overlaytests/OverlayAppFiltered/Android.mk @@ -3,8 +3,6 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES += legacy-test - LOCAL_SDK_VERSION := system_current LOCAL_PACKAGE_NAME := com.android.overlaytest.filtered_app_overlay diff --git a/core/tests/overlaytests/OverlayAppFirst/Android.mk b/core/tests/overlaytests/OverlayAppFirst/Android.mk index 51f4487fb25f..bf9416c279be 100644 --- a/core/tests/overlaytests/OverlayAppFirst/Android.mk +++ b/core/tests/overlaytests/OverlayAppFirst/Android.mk @@ -3,8 +3,6 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES += legacy-test - LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := com.android.overlaytest.first_app_overlay diff --git a/core/tests/overlaytests/OverlayAppSecond/Android.mk b/core/tests/overlaytests/OverlayAppSecond/Android.mk index b3cfd1817cbf..bb7d142d6809 100644 --- a/core/tests/overlaytests/OverlayAppSecond/Android.mk +++ b/core/tests/overlaytests/OverlayAppSecond/Android.mk @@ -3,8 +3,6 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES += legacy-test - LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := com.android.overlaytest.second_app_overlay diff --git a/core/tests/overlaytests/OverlayTest/Android.mk b/core/tests/overlaytests/OverlayTest/Android.mk index 964348fab881..5fe7b917102e 100644 --- a/core/tests/overlaytests/OverlayTest/Android.mk +++ b/core/tests/overlaytests/OverlayTest/Android.mk @@ -7,7 +7,7 @@ LOCAL_PACKAGE_NAME := OverlayTest LOCAL_DEX_PREOPT := false -LOCAL_JAVA_LIBRARIES += legacy-test +LOCAL_JAVA_LIBRARIES += android.test.base LOCAL_MODULE_PATH := $(TARGET_OUT)/app diff --git a/core/tests/overlaytests/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/OverlayTestOverlay/Android.mk index 5265d9169f7f..ed330467f68a 100644 --- a/core/tests/overlaytests/OverlayTestOverlay/Android.mk +++ b/core/tests/overlaytests/OverlayTestOverlay/Android.mk @@ -3,8 +3,6 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES += legacy-test - LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := com.android.overlaytest.overlay diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 83c82af4385c..94a05b2f90e9 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -427,7 +427,6 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu ATRACE_CALL(); constexpr const int kMaxIterations = 20; - *out_last_reference = 0u; for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index a77c4b9ad721..b033137b4764 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -210,7 +210,7 @@ class AssetManager2 { // are OR'd together with `in_out_flags`. // `in_out_config` is populated with the configuration for which the resolved value was defined. // `out_last_reference` is populated with the last reference ID before resolving to an actual - // value. + // value. This is only initialized if the passed in `in_out_value` is a reference. // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index ab1a22ed9dc3..567adfebd31f 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -319,7 +319,7 @@ TEST_F(AssetManager2Test, ResolveReferenceToResource) { EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); EXPECT_EQ(basic::R::integer::ref2, value.data); - uint32_t last_ref; + uint32_t last_ref = 0u; cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); ASSERT_NE(kInvalidCookie, cookie); EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); @@ -342,7 +342,7 @@ TEST_F(AssetManager2Test, ResolveReferenceToBag) { EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); EXPECT_EQ(basic::R::array::integerArray1, value.data); - uint32_t last_ref; + uint32_t last_ref = 0u; cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); ASSERT_NE(kInvalidCookie, cookie); EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); @@ -350,6 +350,25 @@ TEST_F(AssetManager2Test, ResolveReferenceToBag) { EXPECT_EQ(basic::R::array::integerArray1, last_ref); } +TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + + ResTable_config selected_config; + memset(&selected_config, 0, sizeof(selected_config)); + + uint32_t flags = 0u; + + // Create some kind of Res_value that is NOT a reference. + Res_value value; + value.dataType = Res_value::TYPE_STRING; + value.data = 0; + + uint32_t last_ref = basic::R::string::test1; + EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref)); + EXPECT_EQ(basic::R::string::test1, last_ref); +} + static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations, const ResTable_config& configuration) { return configurations.count(configuration) > 0; diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 5c577aecca28..96e4fcf37fa3 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -417,3 +417,15 @@ cc_benchmark { "tests/microbench/TaskManagerBench.cpp", ], } + +// ---------------------------------------- +// Phony target to build benchmarks for PGO +// ---------------------------------------- + +phony { + name: "pgo-targets-hwui", + required: [ + "hwuimicro", + "hwuimacro", + ] +} diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 508869a0cbdd..eb0d161d71d1 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -738,6 +738,7 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& p // care of all alignment. SkPaint paintCopy(paint); paintCopy.setTextAlign(SkPaint::kLeft_Align); + SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); SkRect bounds = SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y); diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index 58c99800875b..6bae80c120ab 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -36,6 +36,7 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { SkPaint textPaint; textPaint.setTextSize(dp(20)); textPaint.setAntiAlias(true); + textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); TestUtils::drawUtf8ToCanvas(&canvas, "not that long long text", textPaint, dp(10), dp(30)); SkPoint pts[2]; diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp index 75b231dd1052..fee0659fa81d 100644 --- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp @@ -42,8 +42,10 @@ public: mBluePaint.setColor(SkColorSetARGB(255, 0, 0, 255)); mBluePaint.setTextSize(padding); + mBluePaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); mGreenPaint.setColor(SkColorSetARGB(255, 0, 255, 0)); mGreenPaint.setTextSize(padding); + mGreenPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); // interleave drawText and drawRect with saveLayer ops for (int i = 0; i < regions; i++, top += smallRectHeight) { diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index dff259f81ec9..7dd271f5daf4 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -350,6 +350,72 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { EXPECT_EQ(3, canvas.getIndex()); } +RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { + class ProjectionTestCanvas : public SkCanvas { + public: + ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {} + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + mDrawCounter++; + } + + int getDrawCounter() { return mDrawCounter; } + + private: + int mDrawCounter = 0; + }; + + auto receiverBackground = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { + properties.setProjectionReceiver(true); + }, + "B"); // a receiver with an empty display list + + auto projectingRipple = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + SkPaint paint; + canvas.drawRect(0, 0, 100, 100, paint); + }, + "P"); + auto child = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { + SkPaint paint; + canvas.drawRect(0, 0, 100, 100, paint); + canvas.drawRenderNode(projectingRipple.get()); + }, + "C"); + auto parent = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [&receiverBackground, &child](RenderProperties& properties, + SkiaRecordingCanvas& canvas) { + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + }, + "A"); + ContextFactory contextFactory; + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); + TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); + DamageAccumulator damageAccumulator; + info.damageAccumulator = &damageAccumulator; + parent->prepareTree(info); + + // parent(A) -> (receiverBackground, child) + // child(C) -> (rect[0, 0, 100, 100], projectingRipple) + // projectingRipple(P) -> (rect[0, 0, 100, 100]) -> projects backwards + // receiverBackground(B) -> (empty) -> projection receiver + + // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection + ProjectionTestCanvas canvas(100, 100); + RenderNodeDrawable drawable(parent.get(), &canvas, true); + canvas.drawDrawable(&drawable); + EXPECT_EQ(2, canvas.getDrawCounter()); +} + RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { /* R is backward projected on B and C is a layer. A diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 7afe267f9c15..e0289f0bf336 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -741,7 +741,7 @@ public final class AudioAttributes implements Parcelable { /** * @hide * Same as {@link #setCapturePreset(int)} but authorizes the use of HOTWORD, - * REMOTE_SUBMIX and RADIO_TUNER. + * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK and VOICE_CALL. * @param preset * @return the same Builder instance. */ @@ -749,7 +749,10 @@ public final class AudioAttributes implements Parcelable { public Builder setInternalCapturePreset(int preset) { if ((preset == MediaRecorder.AudioSource.HOTWORD) || (preset == MediaRecorder.AudioSource.REMOTE_SUBMIX) - || (preset == MediaRecorder.AudioSource.RADIO_TUNER)) { + || (preset == MediaRecorder.AudioSource.RADIO_TUNER) + || (preset == MediaRecorder.AudioSource.VOICE_DOWNLINK) + || (preset == MediaRecorder.AudioSource.VOICE_UPLINK) + || (preset == MediaRecorder.AudioSource.VOICE_CALL)) { mSource = preset; } else { setCapturePreset(preset); diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 0906ba50f7df..27784e96d9f9 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -1516,66 +1516,13 @@ public class AudioRecord implements AudioRouting } /** - * Helper class to handle the forwarding of native events to the appropriate listener - * (potentially) handled in a different thread - */ - private class NativeRoutingEventHandlerDelegate { - private final Handler mHandler; - - NativeRoutingEventHandlerDelegate(final AudioRecord record, - final AudioRouting.OnRoutingChangedListener listener, - Handler handler) { - // find the looper for our new event handler - Looper looper; - if (handler != null) { - looper = handler.getLooper(); - } else { - // no given handler, use the looper the AudioRecord was created in - looper = mInitializationLooper; - } - - // construct the event handler with this looper - if (looper != null) { - // implement the event handler delegate - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - if (record == null) { - return; - } - switch(msg.what) { - case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE: - if (listener != null) { - listener.onRoutingChanged(record); - } - break; - default: - loge("Unknown native event type: " + msg.what); - break; - } - } - }; - } else { - mHandler = null; - } - } - - Handler getHandler() { - return mHandler; - } - } - - /** * Sends device list change notification to all listeners. */ private void broadcastRoutingChange() { AudioManager.resetAudioPortGeneration(); synchronized (mRoutingChangeListeners) { for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) { - Handler handler = delegate.getHandler(); - if (handler != null) { - handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE); - } + delegate.notifyClient(); } } } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 50145f8a9886..e535fdf53d01 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -2856,10 +2856,7 @@ public class AudioTrack extends PlayerBase AudioManager.resetAudioPortGeneration(); synchronized (mRoutingChangeListeners) { for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) { - Handler handler = delegate.getHandler(); - if (handler != null) { - handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE); - } + delegate.notifyClient(); } } } @@ -2943,56 +2940,6 @@ public class AudioTrack extends PlayerBase } } - /** - * Helper class to handle the forwarding of native events to the appropriate listener - * (potentially) handled in a different thread - */ - private class NativeRoutingEventHandlerDelegate { - private final Handler mHandler; - - NativeRoutingEventHandlerDelegate(final AudioTrack track, - final AudioRouting.OnRoutingChangedListener listener, - Handler handler) { - // find the looper for our new event handler - Looper looper; - if (handler != null) { - looper = handler.getLooper(); - } else { - // no given handler, use the looper the AudioTrack was created in - looper = mInitializationLooper; - } - - // construct the event handler with this looper - if (looper != null) { - // implement the event handler delegate - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - if (track == null) { - return; - } - switch(msg.what) { - case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE: - if (listener != null) { - listener.onRoutingChanged(track); - } - break; - default: - loge("Unknown native event type: " + msg.what); - break; - } - } - }; - } else { - mHandler = null; - } - } - - Handler getHandler() { - return mHandler; - } - } - //--------------------------------------------------------- // Methods for IPlayer interface //-------------------- diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index c78c99f7a228..1019580589ab 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -640,7 +640,6 @@ public class ImageReader implements AutoCloseable { * The ImageReader continues to be usable after this call, but may need to reallocate buffers * when more buffers are needed for rendering. * </p> - * @hide */ public void discardFreeBuffers() { synchronized (mCloseLock) { diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 649c091b1fd4..1bc3dfa4bdbc 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1514,7 +1514,8 @@ public class MediaPlayer extends PlayerBase if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { enableNativeRoutingCallbacksLocked(true); mRoutingChangeListeners.put( - listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); + listener, new NativeRoutingEventHandlerDelegate(this, listener, + handler != null ? handler : mEventHandler)); } } } @@ -1535,36 +1536,6 @@ public class MediaPlayer extends PlayerBase } } - /** - * Helper class to handle the forwarding of native events to the appropriate listener - * (potentially) handled in a different thread - */ - private class NativeRoutingEventHandlerDelegate { - private MediaPlayer mMediaPlayer; - private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; - private Handler mHandler; - - NativeRoutingEventHandlerDelegate(final MediaPlayer mediaPlayer, - final AudioRouting.OnRoutingChangedListener listener, Handler handler) { - mMediaPlayer = mediaPlayer; - mOnRoutingChangedListener = listener; - mHandler = handler != null ? handler : mEventHandler; - } - - void notifyClient() { - if (mHandler != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - if (mOnRoutingChangedListener != null) { - mOnRoutingChangedListener.onRoutingChanged(mMediaPlayer); - } - } - }); - } - } - } - private native final boolean native_setOutputDevice(int deviceId); private native final int native_getRoutedDeviceId(); private native final void native_enableDeviceCallback(boolean enabled); diff --git a/media/java/android/media/NativeRoutingEventHandlerDelegate.java b/media/java/android/media/NativeRoutingEventHandlerDelegate.java new file mode 100644 index 000000000000..9a6baf17e860 --- /dev/null +++ b/media/java/android/media/NativeRoutingEventHandlerDelegate.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 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 android.media; + +import android.os.Handler; + +/** + * Helper class {@link AudioTrack}, {@link AudioRecord}, {@link MediaPlayer} and {@link MediaRecorder} + * to handle the forwarding of native events to the appropriate listener + * (potentially) handled in a different thread. + * @hide + */ +class NativeRoutingEventHandlerDelegate { + private AudioRouting mAudioRouting; + private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; + private Handler mHandler; + + NativeRoutingEventHandlerDelegate(final AudioRouting audioRouting, + final AudioRouting.OnRoutingChangedListener listener, Handler handler) { + mAudioRouting = audioRouting; + mOnRoutingChangedListener = listener; + mHandler = handler; + } + + void notifyClient() { + if (mHandler != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mOnRoutingChangedListener != null) { + mOnRoutingChangedListener.onRoutingChanged(mAudioRouting); + } + } + }); + } + } +} diff --git a/media/mca/filterfw/Android.mk b/media/mca/filterfw/Android.mk index 334f4e215d96..37f1e13f7827 100644 --- a/media/mca/filterfw/Android.mk +++ b/media/mca/filterfw/Android.mk @@ -26,6 +26,8 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE := libfilterfw +LOCAL_CFLAGS := -Wall -Werror + LOCAL_MODULE_TAGS := optional LOCAL_WHOLE_STATIC_LIBRARIES := libfilterfw_jni \ diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java index 56a675acf082..15ea36789e2c 100644 --- a/obex/javax/obex/ServerOperation.java +++ b/obex/javax/obex/ServerOperation.java @@ -195,7 +195,12 @@ public final class ServerOperation implements Operation, BaseStream { if(!handleObexPacket(packet)) { return; } - if (!mHasBody) { + /* Don't Pre-Send continue when Remote requested for SRM + * Let the Application confirm. + */ + if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled + + " not hasBody case: " + mHasBody); + if (!mHasBody && !mSrmEnabled) { while ((!mGetOperation) && (!finalBitSet)) { sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); if (mPrivateInput.available() > 0) { @@ -204,8 +209,13 @@ public final class ServerOperation implements Operation, BaseStream { } } } - - while ((!mGetOperation) && (!finalBitSet) && (mPrivateInput.available() == 0)) { + /* Don't Pre-Send continue when Remote requested for SRM + * Let the Application confirm. + */ + if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled + + " not finalPacket: " + finalBitSet + " not GETOp Case: " + mGetOperation); + while ((!mSrmEnabled) && (!mGetOperation) && (!finalBitSet) + && (mPrivateInput.available() == 0)) { sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); if (mPrivateInput.available() > 0) { break; diff --git a/packages/CompanionDeviceManager/res/drawable/dialog_background.xml b/packages/CompanionDeviceManager/res/drawable/dialog_background.xml index af2c83f7d8b3..a017f4167907 100644 --- a/packages/CompanionDeviceManager/res/drawable/dialog_background.xml +++ b/packages/CompanionDeviceManager/res/drawable/dialog_background.xml @@ -16,7 +16,7 @@ <inset xmlns:android="http://schemas.android.com/apk/res/android"> <shape android:shape="rectangle"> - <corners android:radius="2dp" /> + <corners android:radius="?android:attr/dialogCornerRadius" /> <solid android:color="?android:attr/colorBackground" /> </shape> </inset> diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java index ee7885d2a077..07033304653c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java @@ -18,7 +18,6 @@ package com.android.settingslib.drawer; import android.content.ComponentName; import android.content.Context; import android.support.annotation.VisibleForTesting; -import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -27,7 +26,6 @@ import android.util.Pair; import com.android.settingslib.applications.InterestingConfigChanges; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -104,10 +102,10 @@ public class CategoryManager { } for (int i = 0; i < mCategories.size(); i++) { DashboardCategory category = mCategories.get(i); - for (int j = 0; j < category.tiles.size(); j++) { - Tile tile = category.tiles.get(j); + for (int j = 0; j < category.getTilesCount(); j++) { + Tile tile = category.getTile(j); if (tileBlacklist.contains(tile.intent.getComponent())) { - category.tiles.remove(j--); + category.removeTile(j--); } } } @@ -181,7 +179,7 @@ public class CategoryManager { newCategory = new DashboardCategory(); categoryByKeyMap.put(newCategoryKey, newCategory); } - newCategory.tiles.add(tile); + newCategory.addTile(tile); } } } @@ -198,7 +196,7 @@ public class CategoryManager { synchronized void sortCategories(Context context, Map<String, DashboardCategory> categoryByKeyMap) { for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) { - sortCategoriesForExternalTiles(context, categoryEntry.getValue()); + categoryEntry.getValue().sortTiles(context.getPackageName()); } } @@ -210,16 +208,16 @@ public class CategoryManager { synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) { for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) { final DashboardCategory category = categoryEntry.getValue(); - final int count = category.tiles.size(); + final int count = category.getTilesCount(); final Set<ComponentName> components = new ArraySet<>(); for (int i = count - 1; i >= 0; i--) { - final Tile tile = category.tiles.get(i); + final Tile tile = category.getTile(i); if (tile.intent == null) { continue; } final ComponentName tileComponent = tile.intent.getComponent(); if (components.contains(tileComponent)) { - category.tiles.remove(i); + category.removeTile(i); } else { components.add(tileComponent); } @@ -234,28 +232,7 @@ public class CategoryManager { */ private synchronized void sortCategoriesForExternalTiles(Context context, DashboardCategory dashboardCategory) { - final String skipPackageName = context.getPackageName(); + dashboardCategory.sortTiles(context.getPackageName()); - // Sort tiles based on [priority, package within priority] - Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> { - final String package1 = tile1.intent.getComponent().getPackageName(); - final String package2 = tile2.intent.getComponent().getPackageName(); - final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2); - // First sort by priority - final int priorityCompare = tile2.priority - tile1.priority; - if (priorityCompare != 0) { - return priorityCompare; - } - // Then sort by package name, skip package take precedence - if (packageCompare != 0) { - if (TextUtils.equals(package1, skipPackageName)) { - return -1; - } - if (TextUtils.equals(package2, skipPackageName)) { - return 1; - } - } - return packageCompare; - }); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java index f6f81682ad6f..a966e8244ba3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java @@ -16,6 +16,8 @@ package com.android.settingslib.drawer; +import static java.lang.String.CASE_INSENSITIVE_ORDER; + import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; @@ -23,6 +25,8 @@ import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; public class DashboardCategory implements Parcelable { @@ -48,39 +52,59 @@ public class DashboardCategory implements Parcelable { /** * List of the category's children */ - public List<Tile> tiles = new ArrayList<>(); - + private List<Tile> mTiles = new ArrayList<>(); + + DashboardCategory(DashboardCategory in) { + if (in != null) { + title = in.title; + key = in.key; + priority = in.priority; + for (Tile tile : in.mTiles) { + mTiles.add(tile); + } + } + } public DashboardCategory() { // Empty } + /** + * Get a copy of the list of the category's children. + * + * Note: the returned list serves as a read-only list. If tiles needs to be added or removed + * from the actual tiles list, it should be done through {@link #addTile}, {@link #removeTile}. + */ + public List<Tile> getTiles() { + return Collections.unmodifiableList(mTiles); + } + public void addTile(Tile tile) { - tiles.add(tile); + mTiles.add(tile); } public void addTile(int n, Tile tile) { - tiles.add(n, tile); + mTiles.add(n, tile); } public void removeTile(Tile tile) { - tiles.remove(tile); + mTiles.remove(tile); } public void removeTile(int n) { - tiles.remove(n); + mTiles.remove(n); } public int getTilesCount() { - return tiles.size(); + return mTiles.size(); } public Tile getTile(int n) { - return tiles.get(n); + return mTiles.get(n); } public boolean containsComponent(ComponentName component) { - for (Tile tile : tiles) { + for (Tile tile : mTiles) { if (TextUtils.equals(tile.intent.getComponent().getClassName(), component.getClassName())) { if (DEBUG) { @@ -95,6 +119,40 @@ public class DashboardCategory implements Parcelable { return false; } + /** + * Sort priority value for tiles in this category. + */ + public void sortTiles() { + Collections.sort(mTiles, TILE_COMPARATOR); + } + + /** + * Sort priority value and package name for tiles in this category. + */ + public void sortTiles(String skipPackageName) { + // Sort mTiles based on [priority, package within priority] + Collections.sort(mTiles, (tile1, tile2) -> { + final String package1 = tile1.intent.getComponent().getPackageName(); + final String package2 = tile2.intent.getComponent().getPackageName(); + final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2); + // First sort by priority + final int priorityCompare = tile2.priority - tile1.priority; + if (priorityCompare != 0) { + return priorityCompare; + } + // Then sort by package name, skip package take precedence + if (packageCompare != 0) { + if (TextUtils.equals(package1, skipPackageName)) { + return -1; + } + if (TextUtils.equals(package2, skipPackageName)) { + return 1; + } + } + return packageCompare; + }); + } + @Override public int describeContents() { return 0; @@ -106,11 +164,11 @@ public class DashboardCategory implements Parcelable { dest.writeString(key); dest.writeInt(priority); - final int count = tiles.size(); + final int count = mTiles.size(); dest.writeInt(count); for (int n = 0; n < count; n++) { - Tile tile = tiles.get(n); + Tile tile = mTiles.get(n); tile.writeToParcel(dest, flags); } } @@ -124,7 +182,7 @@ public class DashboardCategory implements Parcelable { for (int n = 0; n < count; n++) { Tile tile = Tile.CREATOR.createFromParcel(in); - tiles.add(tile); + mTiles.add(tile); } } @@ -141,4 +199,13 @@ public class DashboardCategory implements Parcelable { return new DashboardCategory[size]; } }; + + public static final Comparator<Tile> TILE_COMPARATOR = + new Comparator<Tile>() { + @Override + public int compare(Tile lhs, Tile rhs) { + return rhs.priority - lhs.priority; + } + }; + } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java index 038dcf8469cf..e986e0f78de4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java @@ -253,7 +253,7 @@ public class TileUtils { } ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values()); for (DashboardCategory category : categories) { - Collections.sort(category.tiles, TILE_COMPARATOR); + category.sortTiles(); } Collections.sort(categories, CATEGORY_COMPARATOR); if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took " @@ -595,14 +595,6 @@ public class TileUtils { return pathSegments.get(0); } - public static final Comparator<Tile> TILE_COMPARATOR = - new Comparator<Tile>() { - @Override - public int compare(Tile lhs, Tile rhs) { - return rhs.priority - lhs.priority; - } - }; - private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR = new Comparator<DashboardCategory>() { @Override diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java index d7eae5f143c3..f099c90fc760 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java @@ -95,8 +95,9 @@ public class CategoryManagerTest { mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap); assertThat(mCategoryByKeyMap.size()).isEqualTo(2); - assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).tiles.size()).isEqualTo(1); - assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1); + assertThat( + mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).getTilesCount()).isEqualTo(1); + assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1); } @Test @@ -114,9 +115,10 @@ public class CategoryManagerTest { // Added 1 more category to category map. assertThat(mCategoryByKeyMap.size()).isEqualTo(2); // The new category map has CATEGORY_NETWORK type now, which contains 1 tile. - assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).tiles.size()).isEqualTo(1); + assertThat( + mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).getTilesCount()).isEqualTo(1); // Old category still exists. - assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1); + assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1); } @Test @@ -136,9 +138,9 @@ public class CategoryManagerTest { tile3.intent = new Intent().setComponent(new ComponentName(testPackage, "class3")); tile3.priority = 200; - category.tiles.add(tile1); - category.tiles.add(tile2); - category.tiles.add(tile3); + category.addTile(tile1); + category.addTile(tile2); + category.addTile(tile3); mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); // Sort their priorities @@ -146,9 +148,9 @@ public class CategoryManagerTest { mCategoryByKeyMap); // Verify they are now sorted. - assertThat(category.tiles.get(0)).isSameAs(tile3); - assertThat(category.tiles.get(1)).isSameAs(tile1); - assertThat(category.tiles.get(2)).isSameAs(tile2); + assertThat(category.getTile(0)).isSameAs(tile3); + assertThat(category.getTile(1)).isSameAs(tile1); + assertThat(category.getTile(2)).isSameAs(tile2); } @Test @@ -169,9 +171,9 @@ public class CategoryManagerTest { tile3.intent = new Intent().setComponent(new ComponentName(testPackage1, "class3")); tile3.priority = 50; - category.tiles.add(tile1); - category.tiles.add(tile2); - category.tiles.add(tile3); + category.addTile(tile1); + category.addTile(tile2); + category.addTile(tile3); mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); // Sort their priorities @@ -179,9 +181,9 @@ public class CategoryManagerTest { mCategoryByKeyMap); // Verify they are now sorted. - assertThat(category.tiles.get(0)).isSameAs(tile2); - assertThat(category.tiles.get(1)).isSameAs(tile1); - assertThat(category.tiles.get(2)).isSameAs(tile3); + assertThat(category.getTile(0)).isSameAs(tile2); + assertThat(category.getTile(1)).isSameAs(tile1); + assertThat(category.getTile(2)).isSameAs(tile3); } @Test @@ -202,9 +204,9 @@ public class CategoryManagerTest { tile3.intent = new Intent().setComponent(new ComponentName(testPackage, "class3")); tile3.priority = 50; - category.tiles.add(tile1); - category.tiles.add(tile2); - category.tiles.add(tile3); + category.addTile(tile1); + category.addTile(tile2); + category.addTile(tile3); mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); // Sort their priorities @@ -212,9 +214,9 @@ public class CategoryManagerTest { mCategoryByKeyMap); // Verify the sorting order is not changed - assertThat(category.tiles.get(0)).isSameAs(tile1); - assertThat(category.tiles.get(1)).isSameAs(tile2); - assertThat(category.tiles.get(2)).isSameAs(tile3); + assertThat(category.getTile(0)).isSameAs(tile1); + assertThat(category.getTile(1)).isSameAs(tile2); + assertThat(category.getTile(2)).isSameAs(tile3); } @Test @@ -236,10 +238,10 @@ public class CategoryManagerTest { final Tile tile4 = new Tile(); tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3")); tile4.priority = -1; - category.tiles.add(tile1); - category.tiles.add(tile2); - category.tiles.add(tile3); - category.tiles.add(tile4); + category.addTile(tile1); + category.addTile(tile2); + category.addTile(tile3); + category.addTile(tile4); mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); // Sort their priorities @@ -247,10 +249,10 @@ public class CategoryManagerTest { mCategoryByKeyMap); // Verify the sorting order is not changed - assertThat(category.tiles.get(0)).isSameAs(tile1); - assertThat(category.tiles.get(1)).isSameAs(tile2); - assertThat(category.tiles.get(2)).isSameAs(tile3); - assertThat(category.tiles.get(3)).isSameAs(tile4); + assertThat(category.getTile(0)).isSameAs(tile1); + assertThat(category.getTile(1)).isSameAs(tile2); + assertThat(category.getTile(2)).isSameAs(tile3); + assertThat(category.getTile(3)).isSameAs(tile4); } @Test @@ -270,9 +272,9 @@ public class CategoryManagerTest { final Tile tile3 = new Tile(); tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3")); tile3.priority = 1; - category.tiles.add(tile1); - category.tiles.add(tile2); - category.tiles.add(tile3); + category.addTile(tile1); + category.addTile(tile2); + category.addTile(tile3); mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); // Sort their priorities @@ -280,9 +282,9 @@ public class CategoryManagerTest { mCategoryByKeyMap); // Verify the sorting order is internal first, follow by package name ordering - assertThat(category.tiles.get(0)).isSameAs(tile2); - assertThat(category.tiles.get(1)).isSameAs(tile3); - assertThat(category.tiles.get(2)).isSameAs(tile1); + assertThat(category.getTile(0)).isSameAs(tile2); + assertThat(category.getTile(1)).isSameAs(tile3); + assertThat(category.getTile(2)).isSameAs(tile1); } @Test @@ -303,14 +305,14 @@ public class CategoryManagerTest { tile3.intent = new Intent().setComponent(new ComponentName(testPackage, "class3")); tile3.priority = 50; - category.tiles.add(tile1); - category.tiles.add(tile2); - category.tiles.add(tile3); + category.addTile(tile1); + category.addTile(tile2); + category.addTile(tile3); mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap); - assertThat(category.tiles.size()).isEqualTo(3); + assertThat(category.getTilesCount()).isEqualTo(3); } @Test @@ -331,13 +333,13 @@ public class CategoryManagerTest { tile3.intent = new Intent().setComponent(new ComponentName(testPackage, "class1")); tile3.priority = 50; - category.tiles.add(tile1); - category.tiles.add(tile2); - category.tiles.add(tile3); + category.addTile(tile1); + category.addTile(tile2); + category.addTile(tile3); mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category); mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap); - assertThat(category.tiles.size()).isEqualTo(1); + assertThat(category.getTilesCount()).isEqualTo(1); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index dad3a28f3638..a395a4ab1477 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -216,7 +216,7 @@ public class TileUtilsTest { List<DashboardCategory> categoryList = TileUtils.getCategories( mContext, cache, false /* categoryDefinedInManifest */, testAction, TileUtils.SETTING_PKG); - assertThat(categoryList.get(0).tiles.get(0).category).isEqualTo(testCategory); + assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory); } @Test diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 258c96cf40be..7fb6ede8b29d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -61,6 +61,7 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.provider.Settings; import android.provider.Settings.Global; +import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -343,7 +344,8 @@ public class SettingsProvider extends ContentProvider { } case Settings.CALL_METHOD_GET_SECURE: { - Setting setting = getSecureSetting(name, requestingUserId); + Setting setting = getSecureSetting(name, requestingUserId, + /*enableOverride=*/ true); return packageValueForCallResult(setting, isTrackingGeneration(args)); } @@ -1073,6 +1075,10 @@ public class SettingsProvider extends ContentProvider { } private Setting getSecureSetting(String name, int requestingUserId) { + return getSecureSetting(name, requestingUserId, /*enableOverride=*/ false); + } + + private Setting getSecureSetting(String name, int requestingUserId, boolean enableOverride) { if (DEBUG) { Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")"); } @@ -1102,6 +1108,14 @@ public class SettingsProvider extends ContentProvider { return getSsaidSettingLocked(callingPkg, owningUserId); } } + if (enableOverride) { + if (Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { + final Setting overridden = getLocationProvidersAllowedSetting(owningUserId); + if (overridden != null) { + return overridden; + } + } + } // Not the SSAID; do a straight lookup synchronized (mLock) { @@ -1190,6 +1204,35 @@ public class SettingsProvider extends ContentProvider { return null; } + private Setting getLocationProvidersAllowedSetting(int owningUserId) { + synchronized (mLock) { + final Setting setting = getGlobalSetting( + Global.LOCATION_GLOBAL_KILL_SWITCH); + if (!"1".equals(setting.getValue())) { + return null; + } + // Global kill-switch is enabled. Return an empty value. + final SettingsState settingsState = mSettingsRegistry.getSettingsLocked( + SETTINGS_TYPE_SECURE, owningUserId); + return settingsState.new Setting( + Secure.LOCATION_PROVIDERS_ALLOWED, + "", // value + "", // tag + "", // default value + "", // package name + false, // from system + "0" // id + ) { + @Override + public boolean update(String value, boolean setDefault, String packageName, + String tag, boolean forceNonSystemPackage) { + Slog.wtf(LOG_TAG, "update shoudln't be called on this instance."); + return false; + } + }; + } + } + private boolean insertSecureSetting(String name, String value, String tag, boolean makeDefault, int requestingUserId, boolean forceNotify) { if (DEBUG) { @@ -2780,6 +2823,12 @@ public class SettingsProvider extends ContentProvider { } mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget(); + + // When the global kill switch is updated, send the change notification for + // the location setting. + if (isGlobalSettingsKey(key) && Global.LOCATION_GLOBAL_KILL_SWITCH.equals(name)) { + notifyLocationChangeForRunningUsers(); + } } private void maybeNotifyProfiles(int type, int userId, Uri uri, String name, @@ -2799,6 +2848,24 @@ public class SettingsProvider extends ContentProvider { } } + private void notifyLocationChangeForRunningUsers() { + final List<UserInfo> users = mUserManager.getUsers(/*excludeDying=*/ true); + + for (int i = 0; i < users.size(); i++) { + final int userId = users.get(i).id; + + if (!mUserManager.isUserRunning(UserHandle.of(userId))) { + continue; + } + + final int key = makeKey(SETTINGS_TYPE_GLOBAL, userId); + final Uri uri = getNotificationUriFor(key, Secure.LOCATION_PROVIDERS_ALLOWED); + + mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, + userId, 0, uri).sendToTarget(); + } + } + private boolean isGlobalSettingsKey(int key) { return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL; } @@ -2885,7 +2952,7 @@ public class SettingsProvider extends ContentProvider { } catch (SecurityException e) { Slog.w(LOG_TAG, "Failed to notify for " + userId + ": " + uri, e); } - if (DEBUG) { + if (DEBUG || true) { Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri); } } break; diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SystemUI/res/drawable/ic_account_circle.xml new file mode 100644 index 000000000000..3c5f01ba85e1 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_account_circle.xml @@ -0,0 +1,24 @@ +<!-- +Copyright (C) 2017 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48.0dp" + android:height="48.0dp" + android:viewportWidth="48.0" + android:viewportHeight="48.0"> + <path + android:pathData="M24,0C10.8,0 0,10.8 0,24s10.8,24 24,24s24,-10.8 24,-24S37.200001,0 24,0zM24,7.2c3.96,0 7.2,3.24 7.2,7.2s-3.24,7.2 -7.2,7.2s-7.2,-3.24 -7.2,-7.2S20.040001,7.2 24,7.2zM24,41.279999c-6,0 -11.28,-3.12 -14.4,-7.68c0.12,-4.8 9.6,-7.44 14.4,-7.44s14.28,2.64 14.4,7.44C35.279999,38.16 30,41.279999 24,41.279999z" + android:fillColor="?attr/wallpaperTextColor"/> +</vector> diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java index 12f75bb2d56c..0219db332eaf 100644 --- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java +++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java @@ -307,8 +307,9 @@ public class PasswordTextView extends View { void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex, int removedCount, int addedCount) { - if (AccessibilityManager.getInstance(mContext).isEnabled() && - (isFocused() || isSelected() && isShown())) { + if (AccessibilityManager.getInstance(mContext).isObservedEventType( + AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) + && (isFocused() || isSelected() && isShown())) { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); event.setFromIndex(fromIndex); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 2b48e0fb32bd..51175d1d7cf0 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -387,7 +387,9 @@ public class PipTouchHandler { } case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_MOVE: { - if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) { + if (mAccessibilityManager.isObservedEventType( + AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) + && !mSendingHoverAccessibilityEvents) { AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); event.setImportantForAccessibility(true); @@ -400,7 +402,9 @@ public class PipTouchHandler { break; } case MotionEvent.ACTION_HOVER_EXIT: { - if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) { + if (mAccessibilityManager.isObservedEventType( + AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) + && mSendingHoverAccessibilityEvents) { AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); event.setImportantForAccessibility(true); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index eef43d292249..a98468017f17 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -625,9 +625,7 @@ public class PipManager implements BasePipManager { @Override public void onTaskStackChanged() { if (DEBUG) Log.d(TAG, "onTaskStackChanged()"); - if (!checkCurrentUserId(mContext, DEBUG)) { - return; - } + if (getState() != STATE_NO_PIP) { boolean hasPip = false; @@ -662,9 +660,7 @@ public class PipManager implements BasePipManager { @Override public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { if (DEBUG) Log.d(TAG, "onActivityPinned()"); - if (!checkCurrentUserId(mContext, DEBUG)) { - return; - } + StackInfo stackInfo = getPinnedStackInfo(); if (stackInfo == null) { Log.w(TAG, "Cannot find pinned stack"); @@ -690,9 +686,7 @@ public class PipManager implements BasePipManager { @Override public void onPinnedActivityRestartAttempt(boolean clearedTask) { if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()"); - if (!checkCurrentUserId(mContext, DEBUG)) { - return; - } + // If PIPed activity is launched again by Launcher or intent, make it fullscreen. movePipToFullscreen(); } @@ -700,9 +694,7 @@ public class PipManager implements BasePipManager { @Override public void onPinnedStackAnimationEnded() { if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()"); - if (!checkCurrentUserId(mContext, DEBUG)) { - return; - } + switch (getState()) { case STATE_PIP_MENU: showPipMenu(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 9400fd8a4db5..663f20678299 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -23,14 +23,12 @@ import static android.view.View.MeasureSpec; import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS; import android.app.ActivityManager; -import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityOptions; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; @@ -89,7 +87,6 @@ import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFutur import com.android.systemui.shared.recents.view.RecentsTransition; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.DividerView; -import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; import com.android.systemui.statusbar.phone.StatusBar; import java.util.ArrayList; @@ -657,13 +654,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // the resize mode already. if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) { EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds)); - showRecents( - false /* triggeredFromAltTab */, - dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS, - false /* animate */, - true /* launchedWhileDockingTask*/, - false /* fromHome */, - DividerView.INVALID_RECENTS_GROW_TARGET); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index cf3cae5a7c04..2d3080b1f7d2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -68,7 +68,6 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.app.AssistUtils; import com.android.internal.os.BackgroundThread; import com.android.systemui.Dependency; -import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImpl; @@ -372,7 +371,7 @@ public class SystemServicesProxy { if (mIam == null) return false; try { - return mIam.isInLockTaskMode(); + return mIam.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED; } catch (RemoteException e) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index e9096447bbf6..991c3c83cbc1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -162,7 +162,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Matrix matrix = new Matrix(); int overlayColor = 0x40FFFFFF; - Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); + Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, data.image.getConfig()); matrix.setTranslate((previewWidth - mImageWidth) / 2, (previewHeight - mImageHeight) / 2); c.setBitmap(picture); c.drawBitmap(data.image, matrix, paint); @@ -171,7 +171,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { // Note, we can't use the preview for the small icon, since it is non-square float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight); - Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888); + Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, data.image.getConfig()); matrix.setScale(scale, scale); matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2, (iconSize - (scale * mImageHeight)) / 2); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java index f5c77f267952..64c52ed6d29f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java @@ -369,7 +369,7 @@ class CarNavigationBarController { private void onFacetClicked(Intent intent, int index) { String packageName = intent.getPackage(); - if (packageName == null) { + if (packageName == null && !intent.getCategories().contains(Intent.CATEGORY_HOME)) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 5c9446ce8672..34486dbcaf43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -250,7 +250,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange } break; case STATE_FACE_UNLOCK: - iconRes = com.android.internal.R.drawable.ic_account_circle; + iconRes = R.drawable.ic_account_circle; break; case STATE_FINGERPRINT: // If screen is off and device asleep, use the draw on animation so the first frame diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 0d41e2029086..383d32765811 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -441,7 +441,8 @@ public class VolumeDialogImpl implements VolumeDialog { .withEndAction(() -> mDialog.dismiss()) .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) .start(); - if (mAccessibilityMgr.isEnabled()) { + if (mAccessibilityMgr.isObservedEventType( + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) { AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); event.setPackageName(mContext.getPackageName()); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 32af29d24aa2..bd6af01f841d 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5013,6 +5013,11 @@ message MetricsEvent { // OS: P FIELD_SELECTION_WIDGET_VERSION = 1262; + // OPEN: Settings > Battery(version 2) + // CATEGORY: SETTINGS + // OS: P + FUELGAUGE_POWER_USAGE_SUMMARY_V2 = 1263; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java index 7e94d7b6f1fe..22d922be11af 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java @@ -20,9 +20,7 @@ import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; -import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; -import android.accessibilityservice.GestureDescription; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; @@ -49,7 +47,6 @@ import android.view.MagnificationSpec; import android.view.View; import android.view.accessibility.AccessibilityCache; import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; @@ -65,6 +62,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -106,7 +104,7 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec int mFeedbackType; - Set<String> mPackageNames = new HashSet<>(); + final Set<String> mPackageNames = new HashSet<>(); boolean mIsDefault; @@ -284,40 +282,98 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec return true; } - public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) { - mEventTypes = info.eventTypes; - mFeedbackType = info.feedbackType; - String[] packageNames = info.packageNames; - if (packageNames != null) { - mPackageNames.addAll(Arrays.asList(packageNames)); + boolean setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) { + boolean somethingChanged = false; + + if (mEventTypes != info.eventTypes) { + mEventTypes = info.eventTypes; + somethingChanged = true; } - mNotificationTimeout = info.notificationTimeout; - mIsDefault = (info.flags & DEFAULT) != 0; - if (supportsFlagForNotImportantViews(info)) { - if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) { - mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; - } else { - mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; + if (mFeedbackType != info.feedbackType) { + mFeedbackType = info.feedbackType; + somethingChanged = true; + } + + final String[] oldPackageNames = mPackageNames.toArray(new String[mPackageNames.size()]); + if (!Arrays.equals(oldPackageNames, info.packageNames)) { + mPackageNames.clear(); + if (info.packageNames != null) { + Collections.addAll(mPackageNames, info.packageNames); } + somethingChanged = true; } - if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) { - mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS; - } else { - mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS; + if (mNotificationTimeout != info.notificationTimeout) { + mNotificationTimeout = info.notificationTimeout; + somethingChanged = true; + } + + final boolean newIsDefault = (info.flags & DEFAULT) != 0; + if (mIsDefault != newIsDefault) { + mIsDefault = newIsDefault; + somethingChanged = true; } - mRequestTouchExplorationMode = (info.flags + if (supportsFlagForNotImportantViews(info)) { + somethingChanged |= updateFetchFlag(info.flags, + AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS); + } + + somethingChanged |= updateFetchFlag(info.flags, + AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS); + + final boolean newRequestTouchExplorationMode = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0; - mRequestFilterKeyEvents = (info.flags + if (mRequestTouchExplorationMode != newRequestTouchExplorationMode) { + mRequestTouchExplorationMode = newRequestTouchExplorationMode; + somethingChanged = true; + } + + final boolean newRequestFilterKeyEvents = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0; - mRetrieveInteractiveWindows = (info.flags + if (mRequestFilterKeyEvents != newRequestFilterKeyEvents) { + mRequestFilterKeyEvents = newRequestFilterKeyEvents; + somethingChanged = true; + } + + final boolean newRetrieveInteractiveWindows = (info.flags & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0; - mCaptureFingerprintGestures = (info.flags + if (mRetrieveInteractiveWindows != newRetrieveInteractiveWindows) { + mRetrieveInteractiveWindows = newRetrieveInteractiveWindows; + somethingChanged = true; + } + + final boolean newCaptureFingerprintGestures = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0; - mRequestAccessibilityButton = (info.flags + if (mCaptureFingerprintGestures != newCaptureFingerprintGestures) { + mCaptureFingerprintGestures = newCaptureFingerprintGestures; + somethingChanged = true; + } + + final boolean newRequestAccessibilityButton = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; + if (mRequestAccessibilityButton != newRequestAccessibilityButton) { + mRequestAccessibilityButton = newRequestAccessibilityButton; + somethingChanged = true; + } + + return somethingChanged; + } + + private boolean updateFetchFlag(int allFlags, int flagToUpdate) { + if ((allFlags & flagToUpdate) != 0) { + if ((mFetchFlags & flagToUpdate) == 0) { + mFetchFlags |= flagToUpdate; + return true; + } + } else { + if ((mFetchFlags & flagToUpdate) != 0) { + mFetchFlags &= ~flagToUpdate; + return true; + } + } + return false; } protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) { @@ -349,14 +405,15 @@ abstract class AccessibilityClientConnection extends IAccessibilityServiceConnec // If the XML manifest had data to configure the service its info // should be already set. In such a case update only the dynamically // configurable properties. + final boolean serviceInfoChanged; AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo; if (oldInfo != null) { oldInfo.updateDynamicallyConfigurableProperties(info); - setDynamicallyConfigurableProperties(oldInfo); + serviceInfoChanged = setDynamicallyConfigurableProperties(oldInfo); } else { - setDynamicallyConfigurableProperties(info); + serviceInfoChanged = setDynamicallyConfigurableProperties(info); } - mSystemSupport.onClientChange(true); + mSystemSupport.onClientChange(serviceInfoChanged); } } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 3554448efe18..8b5c85a72d7a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2400,7 +2400,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void announceNewUserIfNeeded() { synchronized (mLock) { UserState userState = getCurrentUserStateLocked(); - if (userState.isHandlingAccessibilityEvents()) { + if (userState.isHandlingAccessibilityEvents() + && userState.isObservedEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT)) { UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); String message = mContext.getString(R.string.user_switched, @@ -3157,13 +3158,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (mWindowsForAccessibilityCallback == null) { return; } + final int userId; + synchronized (mLock) { + userId = mCurrentUserId; + final UserState userState = getUserStateLocked(userId); + if (!userState.isObservedEventType(AccessibilityEvent.TYPE_WINDOWS_CHANGED)) { + return; + } + } final long identity = Binder.clearCallingIdentity(); try { // Let the client know the windows changed. AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_WINDOWS_CHANGED); event.setEventTime(SystemClock.uptimeMillis()); - sendAccessibilityEvent(event, mCurrentUserId); + sendAccessibilityEvent(event, userId); } finally { Binder.restoreCallingIdentity(identity); } @@ -3368,6 +3377,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mUserId = userId; } + public boolean isObservedEventType(@AccessibilityEvent.EventType int type) { + return (mLastSentRelevantEventTypes & type) != 0; + } + public int getClientState() { int clientState = 0; final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked() diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index 3419b809f1b8..62017e879584 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -791,7 +791,7 @@ class TouchExplorer extends BaseEventStreamTransformation */ private void sendAccessibilityEvent(int type) { AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); - if (accessibilityManager.isEnabled()) { + if (accessibilityManager.isObservedEventType(type)) { AccessibilityEvent event = AccessibilityEvent.obtain(type); event.setWindowId(mAms.getActiveWindowId()); accessibilityManager.sendAccessibilityEvent(event); diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index c34c30cf6e43..04279a31d2be 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -2150,31 +2150,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { (int)((onDuration / (1000 * 60)) % 60), (int)((onDuration / 1000) % 60), (int)(onDuration % 1000)); - writer.println(" time since enabled: " + onDurationString + "\n"); + writer.println(" time since enabled: " + onDurationString); } if (mActiveLogs.size() == 0) { - writer.println("Bluetooth never enabled!"); + writer.println("\nBluetooth never enabled!"); } else { - writer.println("Enable log:"); + writer.println("\nEnable log:"); for (ActiveLog log : mActiveLogs) { writer.println(" " + log); } } - writer.println("Bluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); + writer.println("\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); if (mCrashes == CRASH_LOG_MAX_SIZE) writer.println("(last " + CRASH_LOG_MAX_SIZE + ")"); for (Long time : mCrashTimestamps) { writer.println(" " + timeToLog(time.longValue())); } - String bleAppString = "No BLE Apps registered."; - if (mBleApps.size() == 1) { - bleAppString = "1 BLE App registered:"; - } else if (mBleApps.size() > 1) { - bleAppString = mBleApps.size() + " BLE Apps registered:"; - } - writer.println("\n" + bleAppString); + writer.println("\n" + mBleApps.size() + " BLE app" + + (mBleApps.size() == 1 ? "" : "s") + "registered"); for (ClientDeathRecipient app : mBleApps.values()) { writer.println(" " + app.getPackageName()); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index ced8621d7beb..bdfd82f440a6 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -501,9 +501,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai windowingMode = WINDOWING_MODE_FULLSCREEN; } + final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack(); + // Take any required action due to us not supporting the preferred windowing mode. if (windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) { - if (display.hasSplitScreenPrimaryStack() + if (alreadyInSplitScreenMode && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) { // Looks like we can't launch in split screen mode, go ahead an dismiss split-screen @@ -577,7 +579,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */); } } finally { - if (mDisplayId == DEFAULT_DISPLAY + if (!alreadyInSplitScreenMode && mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { // Make sure recents stack exist when creating a dock stack as it normally needs to // be on the other side of the docked stack and we make visibility decisions based @@ -1678,12 +1680,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return true; } - /** Returns true if the stack is currently considered visible. */ - boolean isVisible() { - return mWindowContainerController != null && mWindowContainerController.isVisible() - && !mForceHidden; - } - boolean isTopStackOnDisplay() { return getDisplay().isTopStack(this); } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 27eb98586e7f..2fc5dda6364e 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -606,21 +606,9 @@ class ActivityStarter { return; } - if (startedActivityStack.inSplitScreenPrimaryWindowingMode()) { - final ActivityStack homeStack = mSupervisor.mHomeStack; - final boolean homeStackVisible = homeStack != null && homeStack.isVisible(); - if (homeStackVisible) { - // We launch an activity while being in home stack, which means either launcher or - // recents into docked stack. We don't want the launched activity to be alone in a - // docked stack, so we want to immediately launch recents too. - if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch."); - mService.mWindowManager.showRecentApps(true /* fromHome */); - } - return; - } - - boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) - == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK) && (mReuseTask != null); + final int clearTaskFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK; + boolean clearedTask = (mLaunchFlags & clearTaskFlags) == clearTaskFlags + && mReuseTask != null; if (startedActivityStack.inPinnedWindowingMode() && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP || clearedTask)) { diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java index e87b4e63c5b0..d77e1a20e0c0 100644 --- a/services/core/java/com/android/server/am/LockTaskController.java +++ b/services/core/java/com/android/server/am/LockTaskController.java @@ -250,7 +250,24 @@ public class LockTaskController { } /** - * @return whether the requested task is allowed to be launched. + * @return whether the requested task is allowed to be locked (either whitelisted, or declares + * lockTaskMode="always" in the manifest). + */ + boolean isTaskWhitelisted(TaskRecord task) { + switch(task.mLockTaskAuth) { + case LOCK_TASK_AUTH_WHITELISTED: + case LOCK_TASK_AUTH_LAUNCHABLE: + case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: + return true; + case LOCK_TASK_AUTH_PINNABLE: + case LOCK_TASK_AUTH_DONT_LOCK: + default: + return false; + } + } + + /** + * @return whether the requested task is disallowed to be launched. */ boolean isLockTaskModeViolation(TaskRecord task) { return isLockTaskModeViolation(task, false); @@ -258,7 +275,7 @@ public class LockTaskController { /** * @param isNewClearTask whether the task would be cleared as part of the operation. - * @return whether the requested task is allowed to be launched. + * @return whether the requested task is disallowed to be launched. */ boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) { if (isLockTaskModeViolationInternal(task, isNewClearTask)) { @@ -275,21 +292,18 @@ public class LockTaskController { // If the task is already at the top and won't be cleared, then allow the operation return false; } - final int lockTaskAuth = task.mLockTaskAuth; - switch (lockTaskAuth) { - case LOCK_TASK_AUTH_DONT_LOCK: - return !mLockTaskModeTasks.isEmpty(); - case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: - case LOCK_TASK_AUTH_LAUNCHABLE: - case LOCK_TASK_AUTH_WHITELISTED: - return false; - case LOCK_TASK_AUTH_PINNABLE: - // Pinnable tasks can't be launched on top of locktask tasks. - return !mLockTaskModeTasks.isEmpty(); - default: - Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth); - return true; + + // Allow recents activity if enabled by policy + if (task.isActivityTypeRecents() && isRecentsAllowed(task.userId)) { + return false; } + + return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty()); + } + + private boolean isRecentsAllowed(int userId) { + return (getLockTaskFeaturesForUser(userId) + & DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS) != 0; } /** @@ -491,6 +505,7 @@ public class LockTaskController { } if (mLockTaskModeTasks.isEmpty()) { + mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.userId); // Start lock task on the handler thread mHandler.post(() -> performStartLockTask( task.intent.getComponent().getPackageName(), diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 1a196013b124..6fb3dbb75f3f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -40,7 +40,7 @@ import android.view.Display; /** * Activity manager code dealing with processes. */ -final class ProcessList { +public final class ProcessList { private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM; // The minimum time we allow between crashes, for us to consider this diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index ebcf8c28079f..abb296e9c8c3 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -502,6 +502,18 @@ class RecentTasks { } } + void onLockTaskModeStateChanged(int lockTaskModeState, int userId) { + if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) { + return; + } + for (int i = mTasks.size() - 1; i >= 0; --i) { + final TaskRecord tr = mTasks.get(i); + if (tr.userId == userId && !mService.mLockTaskController.isTaskWhitelisted(tr)) { + remove(tr); + } + } + } + void removeTasksByPackageName(String packageName, int userId) { for (int i = mTasks.size() - 1; i >= 0; --i) { final TaskRecord tr = mTasks.get(i); diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java new file mode 100644 index 000000000000..41d9feb97c4e --- /dev/null +++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java @@ -0,0 +1,226 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import android.content.Context; +import android.hardware.contexthub.V1_0.ContextHubMsg; +import android.hardware.contexthub.V1_0.IContexthub; +import android.hardware.contexthub.V1_0.Result; +import android.hardware.location.ContextHubTransaction; +import android.hardware.location.IContextHubClient; +import android.hardware.location.IContextHubClientCallback; +import android.hardware.location.NanoAppMessage; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * A class that acts as a broker for the ContextHubClient, which handles messaging and life-cycle + * notification callbacks. This class implements the IContextHubClient object, and the implemented + * APIs must be thread-safe. + * + * @hide + */ +public class ContextHubClientBroker extends IContextHubClient.Stub + implements IBinder.DeathRecipient { + private static final String TAG = "ContextHubClientBroker"; + + /* + * The context of the service. + */ + private final Context mContext; + + /* + * The proxy to talk to the Context Hub HAL. + */ + private final IContexthub mContextHubProxy; + + /* + * The manager that registered this client. + */ + private final ContextHubClientManager mClientManager; + + /* + * The ID of the hub that this client is attached to. + */ + private final int mAttachedContextHubId; + + /* + * The host end point ID of this client. + */ + private final short mHostEndPointId; + + /* + * The remote callback interface for this client. + */ + private final IContextHubClientCallback mCallbackInterface; + + /* + * false if the connection has been closed by the client, true otherwise. + */ + private final AtomicBoolean mConnectionOpen = new AtomicBoolean(true); + + /* package */ ContextHubClientBroker( + Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager, + int contextHubId, short hostEndPointId, IContextHubClientCallback callback) { + mContext = context; + mContextHubProxy = contextHubProxy; + mClientManager = clientManager; + mAttachedContextHubId = contextHubId; + mHostEndPointId = hostEndPointId; + mCallbackInterface = callback; + } + + /** + * Attaches a death recipient for this client + * + * @throws RemoteException if the client has already died + */ + /* package */ void attachDeathRecipient() throws RemoteException { + mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */); + } + + /** + * Sends from this client to a nanoapp. + * + * @param message the message to send + * @return the error code of sending the message + */ + @ContextHubTransaction.Result + @Override + public int sendMessageToNanoApp(NanoAppMessage message) { + ContextHubServiceUtil.checkPermissions(mContext); + + int result; + if (mConnectionOpen.get()) { + ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage( + mHostEndPointId, message); + + try { + result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = " + + mAttachedContextHubId + ")", e); + result = Result.UNKNOWN_FAILURE; + } + } else { + Log.e(TAG, "Failed to send message to nanoapp: client connection is closed"); + result = Result.UNKNOWN_FAILURE; + } + + return ContextHubServiceUtil.toTransactionResult(result); + } + + /** + * Closes the connection for this client with the service. + */ + @Override + public void close() { + if (mConnectionOpen.getAndSet(false)) { + mClientManager.unregisterClient(mHostEndPointId); + } + } + + /** + * Invoked when the underlying binder of this broker has died at the client process. + */ + public void binderDied() { + try { + IContextHubClient.Stub.asInterface(this).close(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while closing client on death", e); + } + } + + /** + * @return the ID of the context hub this client is attached to + */ + /* package */ int getAttachedContextHubId() { + return mAttachedContextHubId; + } + + /** + * @return the host endpoint ID of this client + */ + /* package */ short getHostEndPointId() { + return mHostEndPointId; + } + + /** + * Sends a message to the client associated with this object. + * + * @param message the message that came from a nanoapp + */ + /* package */ void sendMessageToClient(NanoAppMessage message) { + if (mConnectionOpen.get()) { + try { + mCallbackInterface.onMessageFromNanoApp(message); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while sending message to client (host endpoint ID = " + + mHostEndPointId + ")", e); + } + } + } + + /** + * Handles a nanoapp load event. + * + * @param nanoAppId the ID of the nanoapp that was loaded. + */ + /* package */ void onNanoAppLoaded(long nanoAppId) { + if (mConnectionOpen.get()) { + try { + mCallbackInterface.onNanoAppLoaded(nanoAppId); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while calling onNanoAppLoaded on client" + + " (host endpoint ID = " + mHostEndPointId + ")", e); + } + } + } + + /** + * Handles a nanoapp unload event. + * + * @param nanoAppId the ID of the nanoapp that was unloaded. + */ + /* package */ void onNanoAppUnloaded(long nanoAppId) { + if (mConnectionOpen.get()) { + try { + mCallbackInterface.onNanoAppUnloaded(nanoAppId); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while calling onNanoAppUnloaded on client" + + " (host endpoint ID = " + mHostEndPointId + ")", e); + } + } + } + + /** + * Handles a hub reset for this client. + */ + /* package */ void onHubReset() { + if (mConnectionOpen.get()) { + try { + mCallbackInterface.onHubReset(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while calling onHubReset on client" + + " (host endpoint ID = " + mHostEndPointId + ")", e); + } + } + } +} diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java new file mode 100644 index 000000000000..d58a7460b23f --- /dev/null +++ b/services/core/java/com/android/server/location/ContextHubClientManager.java @@ -0,0 +1,237 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import android.content.Context; +import android.hardware.contexthub.V1_0.ContextHubMsg; +import android.hardware.contexthub.V1_0.IContexthub; +import android.hardware.location.IContextHubClient; +import android.hardware.location.IContextHubClientCallback; +import android.hardware.location.NanoAppMessage; +import android.os.RemoteException; +import android.util.Log; + +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +/** + * A class that manages registration/unregistration of clients and manages messages to/from clients. + * + * @hide + */ +/* package */ class ContextHubClientManager { + private static final String TAG = "ContextHubClientManager"; + + /* + * The maximum host endpoint ID value that a client can be assigned. + */ + private static final int MAX_CLIENT_ID = 0x7fff; + + /* + * Local flag to enable debug logging. + */ + private static final boolean DEBUG_LOG_ENABLED = true; + + /* + * The context of the service. + */ + private final Context mContext; + + /* + * The proxy to talk to the Context Hub. + */ + private final IContexthub mContextHubProxy; + + /* + * A mapping of host endpoint IDs to the ContextHubClientBroker object of registered clients. + * A concurrent data structure is used since the registration/unregistration can occur in + * multiple threads. + */ + private final ConcurrentHashMap<Short, ContextHubClientBroker> mHostEndPointIdToClientMap = + new ConcurrentHashMap<>(); + + /* + * The next host endpoint ID to start iterating for the next available host endpoint ID. + */ + private int mNextHostEndpointId = 0; + + /* package */ ContextHubClientManager( + Context context, IContexthub contextHubProxy) { + mContext = context; + mContextHubProxy = contextHubProxy; + } + + /** + * Registers a new client with the service. + * + * @param clientCallback the callback interface of the client to register + * @param contextHubId the ID of the hub this client is attached to + * + * @return the client interface + * + * @throws IllegalStateException if max number of clients have already registered + */ + /* package */ IContextHubClient registerClient( + IContextHubClientCallback clientCallback, int contextHubId) { + ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId); + + try { + broker.attachDeathRecipient(); + } catch (RemoteException e) { + // The client process has died, so we close the connection and return null. + Log.e(TAG, "Failed to attach death recipient to client"); + broker.close(); + return null; + } + + Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId()); + return IContextHubClient.Stub.asInterface(broker); + } + + /** + * Handles a message sent from a nanoapp. + * + * @param contextHubId the ID of the hub where the nanoapp sent the message from + * @param message the message send by a nanoapp + */ + /* package */ void onMessageFromNanoApp(int contextHubId, ContextHubMsg message) { + NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message); + + if (DEBUG_LOG_ENABLED) { + String targetAudience = clientMessage.isBroadcastMessage() ? "broadcast" : "unicast"; + Log.v(TAG, "Received a " + targetAudience + " message from nanoapp 0x" + + Long.toHexString(clientMessage.getNanoAppId())); + } + + if (clientMessage.isBroadcastMessage()) { + broadcastMessage(contextHubId, clientMessage); + } else { + ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(message.hostEndPoint); + if (proxy != null) { + proxy.sendMessageToClient(clientMessage); + } else { + Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = " + + message.hostEndPoint + ")"); + } + } + } + + /** + * Unregisters a client from the service. + * + * This method should be invoked as a result of a client calling the ContextHubClient.close(), + * or if the client process has died. + * + * @param hostEndPointId the host endpoint ID of the client that has died + */ + /* package */ void unregisterClient(short hostEndPointId) { + if (mHostEndPointIdToClientMap.remove(hostEndPointId) != null) { + Log.d(TAG, "Unregistered client with host endpoint ID " + hostEndPointId); + } else { + Log.e(TAG, "Cannot unregister non-existing client with host endpoint ID " + + hostEndPointId); + } + } + + /** + * Handles a nanoapp load event. + * + * @param contextHubId the ID of the hub where the nanoapp was loaded. + * @param nanoAppId the ID of the nanoapp that was loaded. + */ + /* package */ void onNanoAppLoaded(int contextHubId, long nanoAppId) { + forEachClientOfHub(contextHubId, client -> client.onNanoAppLoaded(nanoAppId)); + } + + /** + * Handles a nanoapp unload event. + * + * @param contextHubId the ID of the hub where the nanoapp was unloaded. + * @param nanoAppId the ID of the nanoapp that was unloaded. + */ + /* package */ void onNanoAppUnloaded(int contextHubId, long nanoAppId) { + forEachClientOfHub(contextHubId, client -> client.onNanoAppUnloaded(nanoAppId)); + } + + /** + * Handles a hub reset. + * + * @param contextHubId the ID of the hub that has reset. + */ + /* package */ void onHubReset(int contextHubId) { + forEachClientOfHub(contextHubId, client -> client.onHubReset()); + } + + /** + * Creates a new ContextHubClientBroker object for a client and registers it with the client + * manager. + * + * @param clientCallback the callback interface of the client to register + * @param contextHubId the ID of the hub this client is attached to + * + * @return the ContextHubClientBroker object + * + * @throws IllegalStateException if max number of clients have already registered + */ + private synchronized ContextHubClientBroker createNewClientBroker( + IContextHubClientCallback clientCallback, int contextHubId) { + if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) { + throw new IllegalStateException("Could not register client - max limit exceeded"); + } + + ContextHubClientBroker broker = null; + int id = mNextHostEndpointId; + for (int i = 0; i <= MAX_CLIENT_ID; i++) { + if (!mHostEndPointIdToClientMap.containsKey(id)) { + broker = new ContextHubClientBroker( + mContext, mContextHubProxy, this, contextHubId, (short)id, clientCallback); + mHostEndPointIdToClientMap.put((short)id, broker); + mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1; + break; + } + + id = (id == MAX_CLIENT_ID) ? 0 : id + 1; + } + + return broker; + } + + /** + * Broadcasts a message from a nanoapp to all clients attached to the associated hub. + * + * @param contextHubId the ID of the hub where the nanoapp sent the message from + * @param message the message send by a nanoapp + */ + private void broadcastMessage(int contextHubId, NanoAppMessage message) { + forEachClientOfHub(contextHubId, client -> client.sendMessageToClient(message)); + } + + /** + * Runs a command for each client that is attached to a hub with the given ID. + * + * @param contextHubId the ID of the hub + * @param callback the command to invoke for the client + */ + private void forEachClientOfHub(int contextHubId, Consumer<ContextHubClientBroker> callback) { + for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) { + if (broker.getAttachedContextHubId() == contextHubId) { + callback.accept(broker); + } + } + } +} diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java index da481a8c2b47..e08c65967b32 100644 --- a/services/core/java/com/android/server/location/ContextHubService.java +++ b/services/core/java/com/android/server/location/ContextHubService.java @@ -16,12 +16,10 @@ package com.android.server.location; -import android.Manifest; import android.content.Context; import android.hardware.contexthub.V1_0.AsyncEventType; import android.hardware.contexthub.V1_0.ContextHub; import android.hardware.contexthub.V1_0.ContextHubMsg; -import android.hardware.contexthub.V1_0.HostEndPoint; import android.hardware.contexthub.V1_0.HubAppInfo; import android.hardware.contexthub.V1_0.IContexthub; import android.hardware.contexthub.V1_0.IContexthubCallback; @@ -29,13 +27,17 @@ import android.hardware.contexthub.V1_0.Result; import android.hardware.contexthub.V1_0.TransactionResult; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubMessage; +import android.hardware.location.ContextHubTransaction; import android.hardware.location.IContextHubCallback; +import android.hardware.location.IContextHubClient; +import android.hardware.location.IContextHubClientCallback; import android.hardware.location.IContextHubService; import android.hardware.location.IContextHubTransactionCallback; import android.hardware.location.NanoApp; import android.hardware.location.NanoAppBinary; import android.hardware.location.NanoAppFilter; import android.hardware.location.NanoAppInstanceInfo; +import android.hardware.location.NanoAppMessage; import android.hardware.location.NanoAppState; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -49,7 +51,9 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; @@ -58,9 +62,6 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ContextHubService extends IContextHubService.Stub { private static final String TAG = "ContextHubService"; - private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE; - private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '" - + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware"; /* * Constants for the type of transaction that is defined by ContextHubService. @@ -104,6 +105,12 @@ public class ContextHubService extends IContextHubService.Stub { // The manager for transaction queue private final ContextHubTransactionManager mTransactionManager; + // The manager for sending messages to/from clients + private final ContextHubClientManager mClientManager; + + // The default client for old API clients + private final Map<Integer, IContextHubClient> mDefaultClientMap; + /** * Class extending the callback to register with a Context Hub. */ @@ -146,21 +153,34 @@ public class ContextHubService extends IContextHubService.Stub { mContextHubProxy = getContextHubProxy(); if (mContextHubProxy == null) { mTransactionManager = null; + mClientManager = null; + mDefaultClientMap = Collections.EMPTY_MAP; mContextHubInfo = new ContextHubInfo[0]; return; } - mTransactionManager = new ContextHubTransactionManager(mContextHubProxy); + mClientManager = new ContextHubClientManager(mContext, mContextHubProxy); + mTransactionManager = new ContextHubTransactionManager(mContextHubProxy, mClientManager); List<ContextHub> hubList; try { hubList = mContextHubProxy.getHubs(); } catch (RemoteException e) { - Log.e(TAG, "RemoteException while getting Context Hub info"); + Log.e(TAG, "RemoteException while getting Context Hub info", e); hubList = Collections.emptyList(); } mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList); + HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>(); + for (ContextHubInfo contextHubInfo : mContextHubInfo) { + int contextHubId = contextHubInfo.getId(); + + IContextHubClient client = mClientManager.registerClient( + createDefaultClientCallback(contextHubId), contextHubId); + defaultClientMap.put(contextHubId, client); + } + mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap); + for (ContextHubInfo contextHubInfo : mContextHubInfo) { int contextHubId = contextHubInfo.getId(); try { @@ -168,7 +188,7 @@ public class ContextHubService extends IContextHubService.Stub { contextHubId, new ContextHubServiceCallback(contextHubId)); } catch (RemoteException e) { Log.e(TAG, "RemoteException while registering service callback for hub (ID = " - + contextHubId + ")"); + + contextHubId + ")", e); } } @@ -185,6 +205,53 @@ public class ContextHubService extends IContextHubService.Stub { } /** + * Creates a default client callback for old API clients. + * + * @param contextHubId the ID of the hub to attach this client to + * @return the internal callback interface + */ + private IContextHubClientCallback createDefaultClientCallback(int contextHubId) { + return new IContextHubClientCallback.Stub() { + @Override + public void onMessageFromNanoApp(NanoAppMessage message) { + int nanoAppInstanceId = + mNanoAppIdToInstanceMap.containsKey(message.getNanoAppId()) ? + mNanoAppIdToInstanceMap.get(message.getNanoAppId()) : -1; + + onMessageReceiptOldApi( + message.getMessageType(), contextHubId, nanoAppInstanceId, + message.getMessageBody()); + } + + @Override + public void onHubReset() { + byte[] data = {TransactionResult.SUCCESS}; + onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data); + } + + @Override + public void onNanoAppAborted(long nanoAppId, int abortCode) { + } + + @Override + public void onNanoAppLoaded(long nanoAppId) { + } + + @Override + public void onNanoAppUnloaded(long nanoAppId) { + } + + @Override + public void onNanoAppEnabled(long nanoAppId) { + } + + @Override + public void onNanoAppDisabled(long nanoAppId) { + } + }; + } + + /** * @return the IContexthub proxy interface */ private IContexthub getContextHubProxy() { @@ -192,7 +259,7 @@ public class ContextHubService extends IContextHubService.Stub { try { proxy = IContexthub.getService(true /* retry */); } catch (RemoteException e) { - Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy"); + Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e); } catch (NoSuchElementException e) { Log.i(TAG, "Context Hub HAL service not found"); } @@ -204,6 +271,7 @@ public class ContextHubService extends IContextHubService.Stub { public int registerCallback(IContextHubCallback callback) throws RemoteException { checkPermissions(); mCallbacksList.register(callback); + Log.d(TAG, "Added callback, total callbacks " + mCallbacksList.getRegisteredCallbackCount()); return 0; @@ -292,7 +360,7 @@ public class ContextHubService extends IContextHubService.Stub { @Override public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) { byte[] data = {(byte) result}; - onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data); + onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data); } }; } @@ -449,41 +517,37 @@ public class ContextHubService extends IContextHubService.Stub { return -1; } if (msg.getData() == null) { - Log.w(TAG, "ContextHubMessage message body cannot be null"); + Log.e(TAG, "ContextHubMessage message body cannot be null"); + return -1; + } + if (!mDefaultClientMap.containsKey(hubHandle)) { + Log.e(TAG, "Hub with ID " + hubHandle + " does not exist"); return -1; } - int result; + boolean success = false; if (nanoAppHandle == OS_APP_INSTANCE) { if (msg.getMsgType() == MSG_QUERY_NANO_APPS) { - result = queryNanoAppsInternal(hubHandle); + success = (queryNanoAppsInternal(hubHandle) == Result.OK); } else { Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType()); - result = Result.BAD_PARAMS; } } else { NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle); if (info != null) { - ContextHubMsg hubMessage = new ContextHubMsg(); - hubMessage.appName = info.getAppId(); - hubMessage.msgType = msg.getMsgType(); - hubMessage.hostEndPoint = HostEndPoint.UNSPECIFIED; - ContextHubServiceUtil.copyToByteArrayList(msg.getData(), hubMessage.msg); - - try { - result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage); - } catch (RemoteException e) { - Log.e(TAG, "Failed to send nanoapp message - RemoteException"); - result = Result.UNKNOWN_FAILURE; - } + NanoAppMessage message = NanoAppMessage.createMessageToNanoApp( + info.getAppId(), msg.getMsgType(), msg.getData()); + + IContextHubClient client = mDefaultClientMap.get(hubHandle); + success = (client.sendMessageToNanoApp(message) == + ContextHubTransaction.TRANSACTION_SUCCESS); } else { Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID " + nanoAppHandle + " does not exist."); - result = Result.BAD_PARAMS; } } - return (result == Result.OK ? 0 : -1); + return success ? 0 : -1; } /** @@ -493,12 +557,7 @@ public class ContextHubService extends IContextHubService.Stub { * @param message the message contents */ private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) { - // TODO(b/67734082): Send to new API clients - byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg); - - int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ? - mNanoAppIdToInstanceMap.get(message.appName) : -1; - onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data); + mClientManager.onMessageFromNanoApp(contextHubId, message); } /** @@ -534,7 +593,7 @@ public class ContextHubService extends IContextHubService.Stub { data[0] = (byte) result; ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId); - onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); + onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); } /** @@ -552,7 +611,7 @@ public class ContextHubService extends IContextHubService.Stub { byte[] data = new byte[1]; data[0] = (byte) result; - onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); + onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); } /** @@ -577,8 +636,7 @@ public class ContextHubService extends IContextHubService.Stub { mTransactionManager.onHubReset(); queryNanoAppsInternal(contextHubId); - byte[] data = {TransactionResult.SUCCESS}; - onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data); + mClientManager.onHubReset(contextHubId); } else { Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = " + eventType + ")"); @@ -641,6 +699,45 @@ public class ContextHubService extends IContextHubService.Stub { } } + /** + * @param contextHubId the hub ID to validate + * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise + */ + private boolean isValidContextHubId(int contextHubId) { + for (ContextHubInfo hubInfo : mContextHubInfo) { + if (hubInfo.getId() == contextHubId) { + return true; + } + } + + return false; + } + + /** + * Creates and registers a client at the service for the specified Context Hub. + * + * @param clientCallback the client interface to register with the service + * @param contextHubId the ID of the hub this client is attached to + * @return the generated client interface, null if registration was unsuccessful + * + * @throws IllegalArgumentException if contextHubId is not a valid ID + * @throws IllegalStateException if max number of clients have already registered + * @throws NullPointerException if clientCallback is null + */ + @Override + public IContextHubClient createClient( + IContextHubClientCallback clientCallback, int contextHubId) throws RemoteException { + checkPermissions(); + if (!isValidContextHubId(contextHubId)) { + throw new IllegalArgumentException("Invalid context hub ID " + contextHubId); + } + if (clientCallback == null) { + throw new NullPointerException("Cannot register client with null callback"); + } + + return mClientManager.registerClient(clientCallback, contextHubId); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -664,10 +761,10 @@ public class ContextHubService extends IContextHubService.Stub { } private void checkPermissions() { - mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE); + ContextHubServiceUtil.checkPermissions(mContext); } - private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) { + private int onMessageReceiptOldApi(int msgType, int hubHandle, int appInstance, byte[] data) { if (data == null) { return -1; } diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java index ddbaf86beff2..6faeb72e2a4d 100644 --- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java +++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java @@ -16,11 +16,15 @@ package com.android.server.location; +import android.Manifest; +import android.content.Context; import android.hardware.contexthub.V1_0.ContextHub; import android.hardware.contexthub.V1_0.ContextHubMsg; import android.hardware.contexthub.V1_0.HostEndPoint; import android.hardware.contexthub.V1_0.HubAppInfo; +import android.hardware.contexthub.V1_0.Result; import android.hardware.location.ContextHubInfo; +import android.hardware.location.ContextHubTransaction; import android.hardware.location.NanoAppBinary; import android.hardware.location.NanoAppMessage; import android.hardware.location.NanoAppState; @@ -34,6 +38,9 @@ import java.util.ArrayList; */ /* package */ class ContextHubServiceUtil { private static final String TAG = "ContextHubServiceUtil"; + private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE; + private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '" + + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware"; /** * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects. @@ -165,4 +172,40 @@ import java.util.ArrayList; message.appName, message.msgType, messageArray, message.hostEndPoint == HostEndPoint.BROADCAST); } + + /** + * Checks for location hardware permissions. + * + * @param context the context of the service + */ + /* package */ + static void checkPermissions(Context context) { + context.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE); + } + + /** + * Helper function to convert from the HAL Result enum error code to the + * ContextHubTransaction.Result type. + * + * @param halResult the Result enum error code + * @return the ContextHubTransaction.Result equivalent + */ + @ContextHubTransaction.Result + /* package */ + static int toTransactionResult(int halResult) { + switch (halResult) { + case Result.OK: + return ContextHubTransaction.TRANSACTION_SUCCESS; + case Result.BAD_PARAMS: + return ContextHubTransaction.TRANSACTION_FAILED_BAD_PARAMS; + case Result.NOT_INIT: + return ContextHubTransaction.TRANSACTION_FAILED_UNINITIALIZED; + case Result.TRANSACTION_PENDING: + return ContextHubTransaction.TRANSACTION_FAILED_PENDING; + case Result.TRANSACTION_FAILED: + case Result.UNKNOWN_FAILURE: + default: /* fall through */ + return ContextHubTransaction.TRANSACTION_FAILED_UNKNOWN; + } + } } diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java index 898b76c9d820..47d9d5686140 100644 --- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java +++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java @@ -58,6 +58,11 @@ import java.util.concurrent.atomic.AtomicInteger; private final IContexthub mContextHubProxy; /* + * The manager for all clients for the service. + */ + private final ContextHubClientManager mClientManager; + + /* * A queue containing the current transactions */ private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>(); @@ -73,8 +78,10 @@ import java.util.concurrent.atomic.AtomicInteger; private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1); private ScheduledFuture<?> mTimeoutFuture = null; - /* package */ ContextHubTransactionManager(IContexthub contextHubProxy) { + /* package */ ContextHubTransactionManager( + IContexthub contextHubProxy, ContextHubClientManager clientManager) { mContextHubProxy = contextHubProxy; + mClientManager = clientManager; } /** @@ -113,6 +120,9 @@ import java.util.concurrent.atomic.AtomicInteger; /* package */ void onTransactionComplete(int result) { try { onCompleteCallback.onTransactionComplete(result); + if (result == Result.OK) { + mClientManager.onNanoAppLoaded(contextHubId, nanoAppBinary.getNanoAppId()); + } } catch (RemoteException e) { Log.e(TAG, "RemoteException while calling client onTransactionComplete"); } @@ -153,6 +163,9 @@ import java.util.concurrent.atomic.AtomicInteger; /* package */ void onTransactionComplete(int result) { try { onCompleteCallback.onTransactionComplete(result); + if (result == Result.OK) { + mClientManager.onNanoAppUnloaded(contextHubId, nanoAppId); + } } catch (RemoteException e) { Log.e(TAG, "RemoteException while calling client onTransactionComplete"); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java new file mode 100644 index 000000000000..2bd9cab313a7 --- /dev/null +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.net; + +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; +import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; +import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; +import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; + +import android.app.ActivityManager; +import android.net.NetworkPolicyManager; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.RingBuffer; +import com.android.server.am.ProcessList; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; + +public class NetworkPolicyLogger { + static final String TAG = "NetworkPolicy"; + + static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); + static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE); + + private static final int MAX_LOG_SIZE = + ActivityManager.isLowRamDeviceStatic() ? 20 : 50; + private static final int MAX_NETWORK_BLOCKED_LOG_SIZE = + ActivityManager.isLowRamDeviceStatic() ? 50 : 100; + + private static final int EVENT_TYPE_GENERIC = 0; + private static final int EVENT_NETWORK_BLOCKED = 1; + private static final int EVENT_UID_STATE_CHANGED = 2; + private static final int EVENT_POLICIES_CHANGED = 3; + private static final int EVENT_METEREDNESS_CHANGED = 4; + private static final int EVENT_USER_STATE_REMOVED = 5; + private static final int EVENT_RESTRICT_BG_CHANGED = 6; + private static final int EVENT_DEVICE_IDLE_MODE_ENABLED = 7; + private static final int EVENT_APP_IDLE_STATE_CHANGED = 8; + private static final int EVENT_PAROLE_STATE_CHANGED = 9; + private static final int EVENT_TEMP_POWER_SAVE_WL_CHANGED = 10; + private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11; + private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12; + + static final int NTWK_BLOCKED_POWER = 0; + static final int NTWK_ALLOWED_NON_METERED = 1; + static final int NTWK_BLOCKED_BLACKLIST = 2; + static final int NTWK_ALLOWED_WHITELIST = 3; + static final int NTWK_ALLOWED_TMP_WHITELIST = 4; + static final int NTWK_BLOCKED_BG_RESTRICT = 5; + static final int NTWK_ALLOWED_DEFAULT = 6; + + private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE); + private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE); + private final LogBuffer mEventsBuffer = new LogBuffer(MAX_LOG_SIZE); + + private final Object mLock = new Object(); + + void networkBlocked(int uid, int reason) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, uid + " is " + getBlockedReason(reason)); + mNetworkBlockedBuffer.networkBlocked(uid, reason); + } + } + + void uidStateChanged(int uid, int procState, long procStateSeq) { + synchronized (mLock) { + if (LOGV) Slog.v(TAG, + uid + " state changed to " + procState + " with seq=" + procStateSeq); + mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq); + } + } + + void event(String msg) { + synchronized (mLock) { + if (LOGV) Slog.v(TAG, msg); + mEventsBuffer.event(msg); + } + } + + void uidPolicyChanged(int uid, int oldPolicy, int newPolicy) { + synchronized (mLock) { + if (LOGV) Slog.v(TAG, getPolicyChangedLog(uid, oldPolicy, newPolicy)); + mEventsBuffer.uidPolicyChanged(uid, oldPolicy, newPolicy); + } + } + + void meterednessChanged(int netId, boolean newMetered) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, getMeterednessChangedLog(netId, newMetered)); + mEventsBuffer.meterednessChanged(netId, newMetered); + } + } + + void removingUserState(int userId) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, getUserRemovedLog(userId)); + mEventsBuffer.userRemoved(userId); + } + } + + void restrictBackgroundChanged(boolean oldValue, boolean newValue) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, + getRestrictBackgroundChangedLog(oldValue, newValue)); + mEventsBuffer.restrictBackgroundChanged(oldValue, newValue); + } + } + + void deviceIdleModeEnabled(boolean enabled) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, getDeviceIdleModeEnabled(enabled)); + mEventsBuffer.deviceIdleModeEnabled(enabled); + } + } + + void appIdleStateChanged(int uid, boolean idle) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, getAppIdleChangedLog(uid, idle)); + mEventsBuffer.appIdleStateChanged(uid, idle); + } + } + + void paroleStateChanged(boolean paroleOn) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn)); + mEventsBuffer.paroleStateChanged(paroleOn); + } + } + + void tempPowerSaveWlChanged(int appId, boolean added) { + synchronized (mLock) { + if (LOGV) Slog.v(TAG, getTempPowerSaveWlChangedLog(appId, added)); + mEventsBuffer.tempPowerSaveWlChanged(appId, added); + } + } + + void uidFirewallRuleChanged(int chain, int uid, int rule) { + synchronized (mLock) { + if (LOGV) Slog.v(TAG, getUidFirewallRuleChangedLog(chain, uid, rule)); + mEventsBuffer.uidFirewallRuleChanged(chain, uid, rule); + } + } + + void firewallChainEnabled(int chain, boolean enabled) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, getFirewallChainEnabledLog(chain, enabled)); + mEventsBuffer.firewallChainEnabled(chain, enabled); + } + } + + void firewallRulesChanged(int chain, int[] uids, int[] rules) { + synchronized (mLock) { + final String log = "Firewall rules changed for " + getFirewallChainName(chain) + + "; uids=" + Arrays.toString(uids) + "; rules=" + Arrays.toString(rules); + if (LOGD) Slog.d(TAG, log); + mEventsBuffer.event(log); + } + } + + void dumpLogs(IndentingPrintWriter pw) { + synchronized (mLock) { + pw.println(); + pw.println("mEventLogs (most recent first):"); + pw.increaseIndent(); + mEventsBuffer.reverseDump(pw); + pw.decreaseIndent(); + + pw.println(); + pw.println("mNetworkBlockedLogs (most recent first):"); + pw.increaseIndent(); + mNetworkBlockedBuffer.reverseDump(pw); + pw.decreaseIndent(); + + pw.println(); + pw.println("mUidStateChangeLogs (most recent first):"); + pw.increaseIndent(); + mUidStateChangeBuffer.reverseDump(pw); + pw.decreaseIndent(); + } + } + + private static String getBlockedReason(int reason) { + switch (reason) { + case NTWK_BLOCKED_POWER: + return "blocked by power restrictions"; + case NTWK_ALLOWED_NON_METERED: + return "allowed on unmetered network"; + case NTWK_BLOCKED_BLACKLIST: + return "blacklisted on metered network"; + case NTWK_ALLOWED_WHITELIST: + return "whitelisted on metered network"; + case NTWK_ALLOWED_TMP_WHITELIST: + return "temporary whitelisted on metered network"; + case NTWK_BLOCKED_BG_RESTRICT: + return "blocked when background is restricted"; + case NTWK_ALLOWED_DEFAULT: + return "allowed by default"; + default: + return String.valueOf(reason); + } + } + + private static String getPolicyChangedLog(int uid, int oldPolicy, int newPolicy) { + return "Policy for " + uid + " changed from " + + NetworkPolicyManager.uidPoliciesToString(oldPolicy) + " to " + + NetworkPolicyManager.uidPoliciesToString(newPolicy); + } + + private static String getMeterednessChangedLog(int netId, boolean newMetered) { + return "Meteredness of netId=" + netId + " changed to " + newMetered; + } + + private static String getUserRemovedLog(int userId) { + return "Remove state for u" + userId; + } + + private static String getRestrictBackgroundChangedLog(boolean oldValue, boolean newValue) { + return "Changed restrictBackground: " + oldValue + "->" + newValue; + } + + private static String getDeviceIdleModeEnabled(boolean enabled) { + return "DeviceIdleMode enabled: " + enabled; + } + + private static String getAppIdleChangedLog(int uid, boolean idle) { + return "App idle state of uid " + uid + ": " + idle; + } + + private static String getParoleStateChanged(boolean paroleOn) { + return "Parole state: " + paroleOn; + } + + private static String getTempPowerSaveWlChangedLog(int appId, boolean added) { + return "temp-power-save whitelist for " + appId + " changed to: " + added; + } + + private static String getUidFirewallRuleChangedLog(int chain, int uid, int rule) { + return String.format("Firewall rule changed: %d-%s-%s", + uid, getFirewallChainName(chain), getFirewallRuleName(rule)); + } + + private static String getFirewallChainEnabledLog(int chain, boolean enabled) { + return "Firewall chain " + getFirewallChainName(chain) + " state: " + enabled; + } + + private static String getFirewallChainName(int chain) { + switch (chain) { + case FIREWALL_CHAIN_DOZABLE: + return FIREWALL_CHAIN_NAME_DOZABLE; + case FIREWALL_CHAIN_STANDBY: + return FIREWALL_CHAIN_NAME_STANDBY; + case FIREWALL_CHAIN_POWERSAVE: + return FIREWALL_CHAIN_NAME_POWERSAVE; + default: + return String.valueOf(chain); + } + } + + private static String getFirewallRuleName(int rule) { + switch (rule) { + case FIREWALL_RULE_DEFAULT: + return "default"; + case FIREWALL_RULE_ALLOW: + return "allow"; + case FIREWALL_RULE_DENY: + return "deny"; + default: + return String.valueOf(rule); + } + } + + private final static class LogBuffer extends RingBuffer<Data> { + private static final SimpleDateFormat sFormatter + = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSS"); + private static final Date sDate = new Date(); + + public LogBuffer(int capacity) { + super(Data.class, capacity); + } + + public void uidStateChanged(int uid, int procState, long procStateSeq) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_UID_STATE_CHANGED; + data.ifield1 = uid; + data.ifield2 = procState; + data.lfield1 = procStateSeq; + data.timeStamp = System.currentTimeMillis(); + } + + public void event(String msg) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_TYPE_GENERIC; + data.sfield1 = msg; + data.timeStamp = System.currentTimeMillis(); + } + + public void networkBlocked(int uid, int reason) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_NETWORK_BLOCKED; + data.ifield1 = uid; + data.ifield2 = reason; + data.timeStamp = System.currentTimeMillis(); + } + + public void uidPolicyChanged(int uid, int oldPolicy, int newPolicy) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_POLICIES_CHANGED; + data.ifield1 = uid; + data.ifield2 = oldPolicy; + data.ifield3 = newPolicy; + data.timeStamp = System.currentTimeMillis(); + } + + public void meterednessChanged(int netId, boolean newMetered) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_METEREDNESS_CHANGED; + data.ifield1 = netId; + data.bfield1 = newMetered; + data.timeStamp = System.currentTimeMillis(); + } + + public void userRemoved(int userId) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_USER_STATE_REMOVED; + data.ifield1 = userId; + data.timeStamp = System.currentTimeMillis(); + } + + public void restrictBackgroundChanged(boolean oldValue, boolean newValue) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_RESTRICT_BG_CHANGED; + data.bfield1 = oldValue; + data.bfield2 = newValue; + data.timeStamp = System.currentTimeMillis(); + } + + public void deviceIdleModeEnabled(boolean enabled) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_DEVICE_IDLE_MODE_ENABLED; + data.bfield1 = enabled; + data.timeStamp = System.currentTimeMillis(); + } + + public void appIdleStateChanged(int uid, boolean idle) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_APP_IDLE_STATE_CHANGED; + data.ifield1 = uid; + data.bfield1 = idle; + data.timeStamp = System.currentTimeMillis(); + } + + public void paroleStateChanged(boolean paroleOn) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_PAROLE_STATE_CHANGED; + data.bfield1 = paroleOn; + data.timeStamp = System.currentTimeMillis(); + } + + public void tempPowerSaveWlChanged(int appId, boolean added) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_TEMP_POWER_SAVE_WL_CHANGED; + data.ifield1 = appId; + data.bfield1 = added; + data.timeStamp = System.currentTimeMillis(); + } + + public void uidFirewallRuleChanged(int chain, int uid, int rule) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_UID_FIREWALL_RULE_CHANGED; + data.ifield1 = chain; + data.ifield2 = uid; + data.ifield3 = rule; + data.timeStamp = System.currentTimeMillis(); + } + + public void firewallChainEnabled(int chain, boolean enabled) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_FIREWALL_CHAIN_ENABLED; + data.ifield1 = chain; + data.bfield1 = enabled; + data.timeStamp = System.currentTimeMillis(); + } + + public void reverseDump(IndentingPrintWriter pw) { + final Data[] allData = toArray(); + for (int i = allData.length - 1; i >= 0; --i) { + if (allData[i] == null) { + pw.println("NULL"); + continue; + } + pw.print(formatDate(allData[i].timeStamp)); + pw.print(" - "); + pw.println(getContent(allData[i])); + } + } + + public String getContent(Data data) { + switch (data.type) { + case EVENT_TYPE_GENERIC: + return data.sfield1; + case EVENT_NETWORK_BLOCKED: + return data.ifield1 + "-" + getBlockedReason(data.ifield2); + case EVENT_UID_STATE_CHANGED: + return data.ifield1 + "-" + ProcessList.makeProcStateString(data.ifield2) + + "-" + data.lfield1; + case EVENT_POLICIES_CHANGED: + return getPolicyChangedLog(data.ifield1, data.ifield2, data.ifield3); + case EVENT_METEREDNESS_CHANGED: + return getMeterednessChangedLog(data.ifield1, data.bfield1); + case EVENT_USER_STATE_REMOVED: + return getUserRemovedLog(data.ifield1); + case EVENT_RESTRICT_BG_CHANGED: + return getRestrictBackgroundChangedLog(data.bfield1, data.bfield2); + case EVENT_DEVICE_IDLE_MODE_ENABLED: + return getDeviceIdleModeEnabled(data.bfield1); + case EVENT_APP_IDLE_STATE_CHANGED: + return getAppIdleChangedLog(data.ifield1, data.bfield1); + case EVENT_PAROLE_STATE_CHANGED: + return getParoleStateChanged(data.bfield1); + case EVENT_TEMP_POWER_SAVE_WL_CHANGED: + return getTempPowerSaveWlChangedLog(data.ifield1, data.bfield1); + case EVENT_UID_FIREWALL_RULE_CHANGED: + return getUidFirewallRuleChangedLog(data.ifield1, data.ifield2, data.ifield3); + case EVENT_FIREWALL_CHAIN_ENABLED: + return getFirewallChainEnabledLog(data.ifield1, data.bfield1); + default: + return String.valueOf(data.type); + } + } + + private String formatDate(long millis) { + sDate.setTime(millis); + return sFormatter.format(sDate); + } + } + + public final static class Data { + int type; + long timeStamp; + + int ifield1; + int ifield2; + int ifield3; + long lfield1; + boolean bfield1; + boolean bfield2; + String sfield1; + + public void reset(){ + sfield1 = null; + } + } +} diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 3fa3cd49d393..fdfe2418bcd2 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -82,6 +82,13 @@ import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; +import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT; +import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED; +import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST; +import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST; +import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT; +import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BLACKLIST; +import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -248,9 +255,9 @@ import java.util.concurrent.TimeUnit; * </ul> */ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { - static final String TAG = "NetworkPolicy"; - private static final boolean LOGD = false; - private static final boolean LOGV = false; + static final String TAG = NetworkPolicyLogger.TAG; + private static final boolean LOGD = NetworkPolicyLogger.LOGD; + private static final boolean LOGV = NetworkPolicyLogger.LOGV; private static final int VERSION_INIT = 1; private static final int VERSION_ADDED_SNOOZE = 2; @@ -265,13 +272,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int VERSION_ADDED_CYCLE = 11; private static final int VERSION_LATEST = VERSION_ADDED_CYCLE; - /** - * Max items written to {@link #ProcStateSeqHistory}. - */ - @VisibleForTesting - public static final int MAX_PROC_STATE_SEQ_HISTORY = - ActivityManager.isLowRamDeviceStatic() ? 50 : 200; - @VisibleForTesting public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING; @VisibleForTesting @@ -471,13 +471,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private ActivityManagerInternal mActivityManagerInternal; - /** - * This is used for debugging purposes. Whenever the IUidObserver.onUidStateChanged is called, - * the uid and procStateSeq will be written to this and will be printed as part of dump. - */ - @VisibleForTesting - public ProcStateSeqHistory mObservedHistory - = new ProcStateSeqHistory(MAX_PROC_STATE_SEQ_HISTORY); + private final NetworkPolicyLogger mLogger = new NetworkPolicyLogger(); // TODO: keep whitelist of system-critical services that should never have // rules enforced, such as system, phone, and radio UIDs. @@ -966,6 +960,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); if ((oldMetered != newMetered) || mNetworkMetered.indexOfKey(network.netId) < 0) { + mLogger.meterednessChanged(network.netId, newMetered); mNetworkMetered.put(network.netId, newMetered); updateNetworkRulesNL(); } @@ -2148,6 +2143,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); if (oldPolicy != policy) { setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); + mLogger.uidPolicyChanged(uid, oldPolicy, policy); } } finally { Binder.restoreCallingIdentity(token); @@ -2168,6 +2164,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { policy |= oldPolicy; if (oldPolicy != policy) { setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); + mLogger.uidPolicyChanged(uid, oldPolicy, policy); } } } @@ -2185,6 +2182,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { policy = oldPolicy & ~policy; if (oldPolicy != policy) { setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); + mLogger.uidPolicyChanged(uid, oldPolicy, policy); } } } @@ -2264,7 +2262,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ boolean removeUserStateUL(int userId, boolean writePolicy) { - if (LOGV) Slog.v(TAG, "removeUserStateUL()"); + mLogger.removingUserState(userId); boolean changed = false; // Remove entries from revoked default restricted background UID whitelist @@ -2429,7 +2427,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void onTetheringChanged(String iface, boolean tethering) { // No need to enforce permission because setRestrictBackground() will do it. - if (LOGD) Log.d(TAG, "onTetherStateChanged(" + iface + ", " + tethering + ")"); synchronized (mUidRulesFirstLock) { if (mRestrictBackground && tethering) { Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver"); @@ -2486,6 +2483,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } sendRestrictBackgroundChangedMsg(); + mLogger.restrictBackgroundChanged(oldRestrictBackground, mRestrictBackground); if (mRestrictBackgroundPowerState.globalBatterySaverEnabled) { mRestrictBackgroundChangedInBsm = true; @@ -2551,6 +2549,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return; } mDeviceIdleMode = enabled; + mLogger.deviceIdleModeEnabled(enabled); if (mSystemReady) { // Device idle change means we need to rebuild rules for all // known apps, so do a global refresh. @@ -2964,10 +2963,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } fout.decreaseIndent(); - fout.println("Observed uid state changes:"); - fout.increaseIndent(); - mObservedHistory.dumpUL(fout); - fout.decreaseIndent(); + mLogger.dumpLogs(fout); } } } @@ -3750,8 +3746,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); - if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle); synchronized (mUidRulesFirstLock) { + mLogger.appIdleStateChanged(uid, idle); updateRuleForAppIdleUL(uid); updateRulesForPowerRestrictionsUL(uid); } @@ -3762,6 +3758,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void onParoleStateChanged(boolean isParoleOn) { synchronized (mUidRulesFirstLock) { + mLogger.paroleStateChanged(isParoleOn); updateRulesForAppIdleParoleUL(); } } @@ -3947,7 +3944,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { synchronized (mUidRulesFirstLock) { // We received a uid state change callback, add it to the history so that it // will be useful for debugging. - mObservedHistory.addProcStateSeqUL(uid, procStateSeq); + mLogger.uidStateChanged(uid, procState, procStateSeq); // Now update the network policy rules as per the updated uid state. updateUidStateUL(uid, procState); // Updating the network rules is done, so notify AMS about this. @@ -4081,6 +4078,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { rules[index] = uidRules.valueAt(index); } mNetworkManager.setFirewallUidRules(chain, uids, rules); + mLogger.firewallRulesChanged(chain, uids, rules); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting firewall uid rules", e); } catch (RemoteException e) { @@ -4107,6 +4105,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { mNetworkManager.setFirewallUidRule(chain, uid, rule); + mLogger.uidFirewallRuleChanged(chain, uid, rule); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting firewall uid rules", e); } catch (RemoteException e) { @@ -4129,6 +4128,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mFirewallChainStates.put(chain, enable); try { mNetworkManager.setFirewallChainEnabled(chain, enable); + mLogger.firewallChainEnabled(chain, enable); } catch (IllegalStateException e) { Log.wtf(TAG, "problem enable firewall chain", e); } catch (RemoteException e) { @@ -4305,30 +4305,30 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { isBackgroundRestricted = mRestrictBackground; } if (hasRule(uidRules, RULE_REJECT_ALL)) { - if (LOGV) logUidStatus(uid, "blocked by power restrictions"); + mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER); return true; } if (!isNetworkMetered) { - if (LOGV) logUidStatus(uid, "allowed on unmetered network"); + mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED); return false; } if (hasRule(uidRules, RULE_REJECT_METERED)) { - if (LOGV) logUidStatus(uid, "blacklisted on metered network"); + mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST); return true; } if (hasRule(uidRules, RULE_ALLOW_METERED)) { - if (LOGV) logUidStatus(uid, "whitelisted on metered network"); + mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST); return false; } if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { - if (LOGV) logUidStatus(uid, "temporary whitelisted on metered network"); + mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST); return false; } if (isBackgroundRestricted) { - if (LOGV) logUidStatus(uid, "blocked when background is restricted"); + mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT); return true; } - if (LOGV) logUidStatus(uid, "allowed by default"); + mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT); return false; } @@ -4379,6 +4379,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void onTempPowerSaveWhitelistChange(int appId, boolean added) { synchronized (mUidRulesFirstLock) { + mLogger.tempPowerSaveWlChanged(appId, added); if (added) { mPowerSaveTempWhitelistAppIds.put(appId, true); } else { @@ -4393,80 +4394,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return (uidRules & rule) != 0; } - private static void logUidStatus(int uid, String descr) { - Slog.d(TAG, String.format("uid %d is %s", uid, descr)); - } - - /** - * This class is used for storing and dumping the last {@link #MAX_PROC_STATE_SEQ_HISTORY} - * (uid, procStateSeq) pairs. - */ - @VisibleForTesting - public static final class ProcStateSeqHistory { - private static final int INVALID_UID = -1; - - /** - * Denotes maximum number of items this history can hold. - */ - private final int mMaxCapacity; - /** - * Used for storing the uid information. - */ - private final int[] mUids; - /** - * Used for storing the sequence numbers associated with {@link #mUids}. - */ - private final long[] mProcStateSeqs; - /** - * Points to the next available slot for writing (uid, procStateSeq) pair. - */ - private int mHistoryNext; - - public ProcStateSeqHistory(int maxCapacity) { - mMaxCapacity = maxCapacity; - mUids = new int[mMaxCapacity]; - Arrays.fill(mUids, INVALID_UID); - mProcStateSeqs = new long[mMaxCapacity]; - } - - @GuardedBy("mUidRulesFirstLock") - public void addProcStateSeqUL(int uid, long procStateSeq) { - mUids[mHistoryNext] = uid; - mProcStateSeqs[mHistoryNext] = procStateSeq; - mHistoryNext = increaseNext(mHistoryNext, 1); - } - - @GuardedBy("mUidRulesFirstLock") - public void dumpUL(IndentingPrintWriter fout) { - if (mUids[0] == INVALID_UID) { - fout.println("NONE"); - return; - } - int index = mHistoryNext; - do { - index = increaseNext(index, -1); - if (mUids[index] == INVALID_UID) { - break; - } - fout.println(getString(mUids[index], mProcStateSeqs[index])); - } while (index != mHistoryNext); - } - - public static String getString(int uid, long procStateSeq) { - return "UID=" + uid + " Seq=" + procStateSeq; - } - - private int increaseNext(int next, int increment) { - next += increment; - if (next >= mMaxCapacity) { - next = 0; - } else if (next < 0) { - next = mMaxCapacity - 1; - } - return next; - } - } - private class NotificationId { private final String mTag; private final int mId; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index cf0140072721..bec6fc2c32f7 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4743,7 +4743,8 @@ public class NotificationManagerService extends SystemService { } void sendAccessibilityEvent(Notification notification, CharSequence packageName) { - if (!mAccessibilityManager.isEnabled()) { + if (!mAccessibilityManager.isObservedEventType( + AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) { return; } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index fb6278a43950..20ec9b5efa98 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -571,7 +571,7 @@ public class PackageManagerServiceUtils { if (!match) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + packageName + - " signatures don't match previously installed version; ignoring!"); + " signatures do not match previously installed version; ignoring!"); } } // Check for shared user signatures @@ -579,16 +579,16 @@ public class PackageManagerServiceUtils { // Already existing package. Make sure signatures match boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures) == PackageManager.SIGNATURE_MATCH; - if (!match) { + if (!match && compareCompat) { match = matchSignaturesCompat( packageName, pkgSetting.sharedUser.signatures, parsedSignatures); } - if (!match && compareCompat) { + if (!match && compareRecover) { match = matchSignaturesRecover( packageName, pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures); compatMatch |= match; } - if (!match && compareRecover) { + if (!match) { throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, "Package " + packageName + " has no signatures that match those in shared user " diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS index b4300a930cdf..d118c4e58a20 100644 --- a/services/core/java/com/android/server/power/OWNERS +++ b/services/core/java/com/android/server/power/OWNERS @@ -1,3 +1,4 @@ michaelwr@google.com per-file BatterySaverPolicy.java=omakoto@google.com +per-file ShutdownThread.java=fkupolov@google.com diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index 6bf725e1a09f..6fb345bcbba8 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -20,10 +20,7 @@ package com.android.server.power; import android.app.AlertDialog; import android.app.Dialog; import android.app.IActivityManager; -import android.app.KeyguardManager; import android.app.ProgressDialog; -import android.app.WallpaperColors; -import android.app.WallpaperManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetoothManager; import android.content.BroadcastReceiver; @@ -31,8 +28,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; import android.media.AudioAttributes; import android.os.FileUtils; import android.os.Handler; @@ -47,8 +42,6 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; -import android.os.storage.IStorageManager; -import android.os.storage.IStorageShutdownObserver; import android.util.ArrayMap; import android.util.Log; import android.util.TimingsTraceLog; @@ -123,7 +116,6 @@ public final class ShutdownThread extends Thread { private static String METRIC_RADIOS = "shutdown_radios"; private static String METRIC_BT = "shutdown_bt"; private static String METRIC_RADIO = "shutdown_radio"; - private static String METRIC_SM = "shutdown_storage_manager"; private final Object mActionDoneSync = new Object(); private boolean mActionDone; @@ -526,54 +518,6 @@ public final class ShutdownThread extends Thread { shutdownTimingLog.traceEnd(); // ShutdownRadios metricEnded(METRIC_RADIOS); - // Shutdown StorageManagerService to ensure media is in a safe state - IStorageShutdownObserver observer = new IStorageShutdownObserver.Stub() { - public void onShutDownComplete(int statusCode) throws RemoteException { - Log.w(TAG, "Result code " + statusCode + " from StorageManagerService.shutdown"); - actionDone(); - } - }; - - Log.i(TAG, "Shutting down StorageManagerService"); - shutdownTimingLog.traceBegin("ShutdownStorageManager"); - metricStarted(METRIC_SM); - - // Set initial variables and time out time. - mActionDone = false; - final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME; - synchronized (mActionDoneSync) { - try { - final IStorageManager storageManager = IStorageManager.Stub.asInterface( - ServiceManager.checkService("mount")); - if (storageManager != null) { - storageManager.shutdown(observer); - } else { - Log.w(TAG, "StorageManagerService unavailable for shutdown"); - } - } catch (Exception e) { - Log.e(TAG, "Exception during StorageManagerService shutdown", e); - } - while (!mActionDone) { - long delay = endShutTime - SystemClock.elapsedRealtime(); - if (delay <= 0) { - Log.w(TAG, "StorageManager shutdown wait timed out"); - break; - } else if (mRebootHasProgressBar) { - int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 * - (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) / - MAX_SHUTDOWN_WAIT_TIME); - status += RADIO_STOP_PERCENT; - sInstance.setRebootProgress(status, null); - } - try { - mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS)); - } catch (InterruptedException e) { - } - } - } - shutdownTimingLog.traceEnd(); // ShutdownStorageManager - metricEnded(METRIC_SM); - if (mRebootHasProgressBar) { sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); @@ -585,6 +529,7 @@ public final class ShutdownThread extends Thread { shutdownTimingLog.traceEnd(); // SystemServerShutdown metricEnded(METRIC_SYSTEM_SERVER); saveMetrics(mReboot); + // Remaining work will be done by init, including vold shutdown rebootOrShutdown(mContext, mReboot, mReason); } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 3772371098d1..00c208d08883 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -15,6 +15,7 @@ */ package com.android.server.stats; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -25,16 +26,22 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.net.NetworkStats; +import android.net.wifi.IWifiManager; +import android.net.wifi.WifiActivityEnergyInfo; +import android.telephony.ModemActivityInfo; +import android.telephony.TelephonyManager; import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.IStatsCompanionService; import android.os.IStatsManager; +import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatsLogEventWrapper; +import android.os.SynchronousResultReceiver; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; @@ -52,6 +59,7 @@ import com.android.server.SystemService; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeoutException; /** * Helper service for statsd (the native stats management service in cmds/statsd/). @@ -60,6 +68,13 @@ import java.util.Map; * @hide */ public class StatsCompanionService extends IStatsCompanionService.Stub { + /** + * How long to wait on an individual subsystem to return its stats. + */ + private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000; + + public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity"; + static final String TAG = "StatsCompanionService"; static final boolean DEBUG = true; public static final String ACTION_TRIGGER_COLLECTION = @@ -79,6 +94,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders; + private IWifiManager mWifiManager = null; + private TelephonyManager mTelephony = null; public StatsCompanionService(Context context) { super(); @@ -389,6 +406,40 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { return ret; } + /** + * Helper method to extract the Parcelable controller info from a + * SynchronousResultReceiver. + */ + private static <T extends Parcelable> T awaitControllerInfo( + @Nullable SynchronousResultReceiver receiver) { + if (receiver == null) { + return null; + } + + try { + final SynchronousResultReceiver.Result result = + receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS); + if (result.bundle != null) { + // This is the final destination for the Bundle. + result.bundle.setDefusable(true); + + final T data = result.bundle.getParcelable( + RESULT_RECEIVER_CONTROLLER_KEY); + if (data != null) { + return data; + } + } + Slog.e(TAG, "no controller energy info supplied for " + receiver.getName()); + } catch (TimeoutException e) { + Slog.w(TAG, "timeout reading " + receiver.getName() + " stats"); + } + return null; + } + + /** + * + * Pulls wifi controller activity energy info from WiFiManager + */ @Override // Binder call public StatsLogEventWrapper[] pullData(int tagId) { enforceCallingPermission(); @@ -509,6 +560,59 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } return ret.toArray(new StatsLogEventWrapper[ret.size()]); } + case StatsLog.WIFI_ACTIVITY_ENERGY_INFO_PULLED: { + List<StatsLogEventWrapper> ret = new ArrayList(); + long token = Binder.clearCallingIdentity(); + if (mWifiManager == null) { + mWifiManager = IWifiManager.Stub.asInterface(ServiceManager.getService( + Context.WIFI_SERVICE)); + } + if (mWifiManager != null) { + try { + SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi"); + mWifiManager.requestActivityInfo(wifiReceiver); + final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6); + e.writeLong(wifiInfo.getTimeStamp()); + e.writeInt(wifiInfo.getStackState()); + e.writeLong(wifiInfo.getControllerTxTimeMillis()); + e.writeLong(wifiInfo.getControllerRxTimeMillis()); + e.writeLong(wifiInfo.getControllerIdleTimeMillis()); + e.writeLong(wifiInfo.getControllerEnergyUsed()); + ret.add(e); + } catch (RemoteException e) { + Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e); + } finally { + Binder.restoreCallingIdentity(token); + } + } + break; + } + case StatsLog.MODEM_ACTIVITY_INFO_PULLED: { + List<StatsLogEventWrapper> ret = new ArrayList(); + long token = Binder.clearCallingIdentity(); + if (mTelephony == null) { + mTelephony = TelephonyManager.from(mContext); + } + if (mTelephony != null) { + SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); + mTelephony.requestModemActivityInfo(modemReceiver); + final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6); + e.writeLong(modemInfo.getTimestamp()); + e.writeLong(modemInfo.getSleepTimeMillis()); + e.writeLong(modemInfo.getIdleTimeMillis()); + e.writeLong(modemInfo.getTxTimeMillis()[0]); + e.writeLong(modemInfo.getTxTimeMillis()[1]); + e.writeLong(modemInfo.getTxTimeMillis()[2]); + e.writeLong(modemInfo.getTxTimeMillis()[3]); + e.writeLong(modemInfo.getTxTimeMillis()[4]); + e.writeLong(modemInfo.getRxTimeMillis()); + e.writeLong(modemInfo.getEnergyUsed()); + ret.add(e); + } + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; @@ -523,6 +627,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { sayHiToStatsd(); // tell statsd that we're ready too and link to it } + @Override + public void triggerUidSnapshot() { + enforceCallingPermission(); + synchronized (sStatsdLock) { + try { + informAllUidsLocked(mContext); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to trigger uid snapshot.", e); + } + } + } + private void enforceCallingPermission() { if (Binder.getCallingPid() == Process.myPid()) { return; diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java index c2a4be55e30a..e7547bf04c08 100644 --- a/services/core/java/com/android/server/wm/StackWindowController.java +++ b/services/core/java/com/android/server/wm/StackWindowController.java @@ -87,12 +87,6 @@ public class StackWindowController } } - public boolean isVisible() { - synchronized (mWindowMap) { - return mContainer != null && mContainer.isVisible(); - } - } - public void reparent(int displayId, Rect outStackBounds, boolean onTop) { synchronized (mWindowMap) { if (mContainer == null) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 43a089373a35..84e475a25187 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -226,7 +226,7 @@ class TaskSnapshotController { final Rect taskFrame = new Rect(); task.getBounds(taskFrame); - final GraphicBuffer buffer = SurfaceControl.captureLayers( + final GraphicBuffer buffer = SurfaceControl.captureLayersToBuffer( task.getSurfaceControl().getHandle(), taskFrame, scaleFraction); if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java new file mode 100644 index 000000000000..dddff8f3ea06 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 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.devicepolicy; + +import android.app.admin.IDevicePolicyManager; + +import com.android.internal.R; +import com.android.server.SystemService; + +/** + * Defines the required interface for IDevicePolicyManager implemenation. + * + * <p>The interface consists of public parts determined by {@link IDevicePolicyManager} and also + * several package private methods required by internal infrastructure. + * + * <p>Whenever adding an AIDL method to {@link IDevicePolicyManager}, an empty override method + * should be added here to avoid build breakage in downstream branches. + */ +abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { + /** + * To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases. + * + * @see {@link SystemService#onBootPhase}. + */ + abstract void systemReady(int phase); + /** + * To be called by {@link DevicePolicyManagerService#Lifecycle} when a new user starts. + * + * @see {@link SystemService#onStartUser} + */ + abstract void handleStartUser(int userId); + /** + * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being unlocked. + * + * @see {@link SystemService#onUnlockUser} + */ + abstract void handleUnlockUser(int userId); + /** + * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being stopped. + * + * @see {@link SystemService#onStopUser} + */ + abstract void handleStopUser(int userId); +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 60c36d1b7203..223778f80b7c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -198,6 +198,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.lang.IllegalStateException; +import java.lang.reflect.Constructor; import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.util.ArrayList; @@ -214,7 +216,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * Implementation of the device policy APIs. */ -public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { +public class DevicePolicyManagerService extends BaseIDevicePolicyManager { protected static final String LOG_TAG = "DevicePolicyManager"; @@ -451,11 +453,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }; public static final class Lifecycle extends SystemService { - private DevicePolicyManagerService mService; + private BaseIDevicePolicyManager mService; public Lifecycle(Context context) { super(context); - mService = new DevicePolicyManagerService(context); + String dpmsClassName = context.getResources() + .getString(R.string.config_deviceSpecificDevicePolicyManagerService); + if (TextUtils.isEmpty(dpmsClassName)) { + dpmsClassName = DevicePolicyManagerService.class.getName(); + } + try { + Class serviceClass = Class.forName(dpmsClassName); + Constructor constructor = serviceClass.getConstructor(Context.class); + mService = (BaseIDevicePolicyManager) constructor.newInstance(context); + } catch (Exception e) { + throw new IllegalStateException( + "Failed to instantiate DevicePolicyManagerService with class name: " + + dpmsClassName, e); + } } @Override @@ -1551,6 +1566,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext = context; } + public boolean hasFeature() { + return getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN); + } + Context createContextAsUser(UserHandle user) throws PackageManager.NameNotFoundException { final String packageName = mContext.getPackageName(); return mContext.createPackageContextAsUser(packageName, 0, user); @@ -1848,8 +1867,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // TODO: why does SecurityLogMonitor need to be created even when mHasFeature == false? mSecurityLogMonitor = new SecurityLogMonitor(this); - mHasFeature = mInjector.getPackageManager() - .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN); + mHasFeature = mInjector.hasFeature(); mIsWatch = mInjector.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_WATCH); mBackgroundHandler = BackgroundThread.getHandler(); @@ -3033,6 +3051,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @VisibleForTesting + @Override void systemReady(int phase) { if (!mHasFeature) { return; @@ -3099,6 +3118,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + @Override void handleStartUser(int userId) { updateScreenCaptureDisabledInWindowManager(userId, getScreenCaptureDisabled(null, userId)); @@ -3107,10 +3127,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { startOwnerService(userId, "start-user"); } + @Override void handleUnlockUser(int userId) { startOwnerService(userId, "unlock-user"); } + @Override void handleStopUser(int userId) { stopOwnerService(userId, "stop-user"); } diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 40788299a6c6..fbcccf0fec2a 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -37,7 +37,6 @@ import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.Time.TIMEZONE_UTC; -import static com.android.server.net.NetworkPolicyManagerService.MAX_PROC_STATE_SEQ_HISTORY; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING; @@ -61,7 +60,6 @@ import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -116,12 +114,10 @@ import android.util.RecurrenceRule; import android.util.TrustedTime; import com.android.internal.telephony.PhoneConstants; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.net.NetworkPolicyManagerService; -import com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHistory; import libcore.io.IoUtils; import libcore.io.Streams; @@ -130,6 +126,7 @@ import com.google.common.util.concurrent.AbstractFuture; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; @@ -142,12 +139,10 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; -import java.io.PrintWriter; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -182,6 +177,7 @@ import java.util.stream.Collectors; "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner" * </code></pre> */ +@Ignore @RunWith(AndroidJUnit4.class) @MediumTest public class NetworkPolicyManagerServiceTest { @@ -191,8 +187,6 @@ public class NetworkPolicyManagerServiceTest { private static final String TEST_IFACE = "test0"; private static final String TEST_SSID = "AndroidAP"; - private static final String LINE_SEPARATOR = System.getProperty("line.separator"); - private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID); /** @@ -1109,14 +1103,6 @@ public class NetworkPolicyManagerServiceTest { final long procStateSeq = 222; callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq); verify(mActivityManagerInternal).notifyNetworkPolicyRulesUpdated(UID_A, procStateSeq); - - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - final IndentingPrintWriter writer = new IndentingPrintWriter( - new PrintWriter(outputStream), " "); - mService.mObservedHistory.dumpUL(writer); - writer.flush(); - assertEquals(ProcStateSeqHistory.getString(UID_A, procStateSeq), - outputStream.toString().trim()); } private void callOnUidStateChanged(int uid, int procState, long procStateSeq) @@ -1129,59 +1115,6 @@ public class NetworkPolicyManagerServiceTest { latch.await(2, TimeUnit.SECONDS); } - @Test - public void testProcStateHistory() { - // Verify dump works correctly with no elements added. - verifyProcStateHistoryDump(0); - - // Add items upto half of the max capacity and verify that dump works correctly. - verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY / 2); - - // Add items upto the max capacity and verify that dump works correctly. - verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY); - - // Add more items than max capacity and verify that dump works correctly. - verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY + MAX_PROC_STATE_SEQ_HISTORY / 2); - - } - - private void verifyProcStateHistoryDump(int count) { - final ProcStateSeqHistory history = new ProcStateSeqHistory(MAX_PROC_STATE_SEQ_HISTORY); - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - final IndentingPrintWriter writer = new IndentingPrintWriter( - new PrintWriter(outputStream), " "); - - if (count == 0) { - // Verify with no uid info written to history. - history.dumpUL(writer); - writer.flush(); - assertEquals("When no uid info is there, dump should contain NONE", - "NONE", outputStream.toString().trim()); - return; - } - - int uid = 111; - long procStateSeq = 222; - // Add count items and verify dump works correctly. - for (int i = 0; i < count; ++i) { - uid++; - procStateSeq++; - history.addProcStateSeqUL(uid, procStateSeq); - } - history.dumpUL(writer); - writer.flush(); - final String[] uidsDump = outputStream.toString().split(LINE_SEPARATOR); - // Dump will have at most MAX_PROC_STATE_SEQ_HISTORY items. - final int expectedCount = (count < MAX_PROC_STATE_SEQ_HISTORY) - ? count : MAX_PROC_STATE_SEQ_HISTORY; - assertEquals(expectedCount, uidsDump.length); - for (int i = 0; i < expectedCount; ++i) { - assertEquals(ProcStateSeqHistory.getString(uid, procStateSeq), uidsDump[i]); - uid--; - procStateSeq--; - } - } - private void assertCycleDayAsExpected(PersistableBundle config, int carrierCycleDay, boolean expectValid) { config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, carrierCycleDay); diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java index 1adfb67cd6ed..54df744ad41b 100644 --- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java @@ -90,6 +90,7 @@ public class LockTaskControllerTest { @Mock private LockPatternUtils mLockPatternUtils; @Mock private LockTaskNotify mLockTaskNotify; @Mock private StatusBarManagerInternal mStatusBarManagerInternal; + @Mock private RecentTasks mRecentTasks; private LockTaskController mLockTaskController; private Context mContext; @@ -110,9 +111,10 @@ public class LockTaskControllerTest { Looper.prepare(); } + mSupervisor.mRecentTasks = mRecentTasks; + mLockTaskController = new LockTaskController(mContext, mSupervisor, new ImmediatelyExecuteHandler()); - mLockTaskController.setWindowManager(mWindowManager); mLockTaskController.mStatusBarService = mStatusBarService; mLockTaskController.mDevicePolicyManager = mDevicePolicyManager; @@ -601,6 +603,8 @@ public class LockTaskControllerTest { eq(mContext.getPackageName())); verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class), eq(mContext.getPackageName())); + // THEN recents should have been notified + verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID)); // THEN the DO/PO should be informed about the operation verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME, TEST_USER_ID); diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 062858d4d237..ca4a210e30cc 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -188,6 +188,11 @@ public abstract class ImsFeature { } /** + * Called when the feature is ready to use. + */ + public abstract void onFeatureReady(); + + /** * Called when the feature is being removed and must be cleaned up. */ public abstract void onFeatureRemoved(); diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java index e790d1460ee0..4e095e3a7003 100644 --- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java @@ -346,6 +346,11 @@ public class MMTelFeature extends ImsFeature { return null; } + @Override + public void onFeatureReady() { + + } + /** * {@inheritDoc} */ diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index a82e60860972..40c5181d6bca 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -36,6 +36,11 @@ public class RcsFeature extends ImsFeature { } @Override + public void onFeatureReady() { + + } + + @Override public void onFeatureRemoved() { } diff --git a/telephony/java/android/telephony/ims/feature/SmsFeature.java b/telephony/java/android/telephony/ims/feature/SmsFeature.java new file mode 100644 index 000000000000..c1366db4a8d6 --- /dev/null +++ b/telephony/java/android/telephony/ims/feature/SmsFeature.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2017 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 android.telephony.ims.feature; + +import android.annotation.SystemApi; +import android.os.RemoteException; +import com.android.ims.internal.IImsSmsFeature; +import com.android.ims.internal.ISmsListener; + +/** + * Base implementation of SMS over IMS functionality. + * + * @hide + */ +public class SmsFeature extends ImsFeature { + /** + * SMS over IMS format is 3gpp. + */ + public static final int IMS_SMS_FORMAT_3GPP = 1; + + /** + * SMS over IMS format is 3gpp2. + */ + public static final int IMS_SMS_FORMAT_3GPP2 = 2; + + /** + * Message was sent successfully. + */ + public static final int SEND_STATUS_OK = 1; + + /** + * IMS provider failed to send the message and platform should not retry falling back to sending + * the message using the radio. + */ + public static final int SEND_STATUS_ERROR = 2; + + /** + * IMS provider failed to send the message and platform should retry again after setting TP-RD bit + * to high. + */ + public static final int SEND_STATUS_ERROR_RETRY = 3; + + /** + * IMS provider failed to send the message and platform should retry falling back to sending + * the message using the radio. + */ + public static final int SEND_STATUS_ERROR_FALLBACK = 4; + + /** + * Message was delivered successfully. + */ + public static final int DELIVER_STATUS_OK = 1; + + /** + * Message was not delivered. + */ + public static final int DELIVER_STATUS_ERROR = 2; + + // Lock for feature synchronization + private final Object mLock = new Object(); + private ISmsListener mSmsListener; + + private final IImsSmsFeature mIImsSmsBinder = new IImsSmsFeature.Stub() { + @Override + public void registerSmsListener(ISmsListener listener) { + synchronized (mLock) { + SmsFeature.this.registerSmsListener(listener); + } + } + + @Override + public void sendSms(int format, int messageRef, boolean retry, byte[] pdu) { + synchronized (mLock) { + SmsFeature.this.sendSms(format, messageRef, retry, pdu); + } + } + + @Override + public void acknowledgeSms(int messageRef, int result) { + synchronized (mLock) { + SmsFeature.this.acknowledgeSms(messageRef, result); + } + } + + @Override + public int getSmsFormat() { + synchronized (mLock) { + return SmsFeature.this.getSmsFormat(); + } + } + }; + + /** + * Registers a listener responsible for handling tasks like delivering messages. + + * @param listener listener to register. + * + * @hide + */ + @SystemApi + public final void registerSmsListener(ISmsListener listener) { + synchronized (mLock) { + mSmsListener = listener; + } + } + + /** + * This method will be triggered by the platform when the user attempts to send an SMS. This + * method should be implemented by the IMS providers to provide implementation of sending an SMS + * over IMS. + * + * @param format the format of the message. One of {@link #IMS_SMS_FORMAT_3GPP} or + * {@link #IMS_SMS_FORMAT_3GPP2} + * @param messageRef the message reference. + * @param retry whether it is a retry of an already attempted message or not. + * @param pdu PDUs representing the contents of the message. + */ + public void sendSms(int format, int messageRef, boolean isRetry, byte[] pdu) { + } + + /** + * This method will be triggered by the platform after {@link #deliverSms(int, byte[])} has been + * called to deliver the result to the IMS provider. It will also be triggered after + * {@link #setSentSmsResult(int, int)} has been called to provide the result of the operation. + * + * @param result Should be {@link #DELIVER_STATUS_OK} if the message was delivered successfully, + * {@link #DELIVER_STATUS_ERROR} otherwise. + * @param messageRef the message reference. + */ + public void acknowledgeSms(int messageRef, int result) { + + } + + /** + * This method should be triggered by the IMS providers when there is an incoming message. The + * platform will deliver the message to the messages database and notify the IMS provider of the + * result by calling {@link #acknowledgeSms(int)}. + * + * This method must not be called before {@link #onFeatureReady()} is called. + * + * @param format the format of the message.One of {@link #IMS_SMS_FORMAT_3GPP} or + * {@link #IMS_SMS_FORMAT_3GPP2} + * @param pdu PDUs representing the contents of the message. + * @throws IllegalStateException if called before {@link #onFeatureReady()} + */ + public final void deliverSms(int format, byte[] pdu) throws IllegalStateException { + // TODO: Guard against NPE/ Check if feature is ready and thrown an exception + // otherwise. + try { + mSmsListener.deliverSms(format, pdu); + } catch (RemoteException e) { + } + } + + /** + * This method should be triggered by the IMS providers to pass the result of the sent message + * to the platform. + * + * This method must not be called before {@link #onFeatureReady()} is called. + * + * @param messageRef the message reference. + * @param result One of {@link #SEND_STATUS_OK}, {@link #SEND_STATUS_ERROR}, + * {@link #SEND_STATUS_ERROR_RETRY}, {@link #SEND_STATUS_ERROR_FALLBACK} + * @throws IllegalStateException if called before {@link #onFeatureReady()} + */ + public final void setSentSmsResult(int messageRef, int result) throws IllegalStateException { + // TODO: Guard against NPE/ Check if feature is ready and thrown an exception + // otherwise. + try { + mSmsListener.setSentSmsResult(messageRef, result); + } catch (RemoteException e) { + } + } + + /** + * Sets the status report of the sent message. + * + * @param format Should be {@link #IMS_SMS_FORMAT_3GPP} or {@link #IMS_SMS_FORMAT_3GPP2} + * @param pdu PDUs representing the content of the status report. + * @throws IllegalStateException if called before {@link #onFeatureReady()} + */ + public final void setSentSmsStatusReport(int format, byte[] pdu) { + // TODO: Guard against NPE/ Check if feature is ready and thrown an exception + // otherwise. + try { + mSmsListener.setSentSmsStatusReport(format, pdu); + } catch (RemoteException e) { + } + } + + /** + * Returns the SMS format. Default is {@link #IMS_SMS_FORMAT_3GPP} unless overridden by IMS + * Provider. + * + * @return sms format. + */ + public int getSmsFormat() { + return IMS_SMS_FORMAT_3GPP; + } + + /** + * {@inheritDoc} + */ + public void onFeatureReady() { + + } + + /** + * {@inheritDoc} + */ + @Override + public void onFeatureRemoved() { + + } + + /** + * @hide + */ + @Override + public final IImsSmsFeature getBinder() { + return mIImsSmsBinder; + } +}
\ No newline at end of file diff --git a/telephony/java/com/android/ims/internal/IImsSmsFeature.aidl b/telephony/java/com/android/ims/internal/IImsSmsFeature.aidl new file mode 100644 index 000000000000..506812882ef5 --- /dev/null +++ b/telephony/java/com/android/ims/internal/IImsSmsFeature.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017 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.ims.internal; + +import com.android.ims.internal.ISmsListener; + +/** + * See SmsFeature for more information. + * + * {@hide} + */ +interface IImsSmsFeature { + void registerSmsListener(in ISmsListener listener); + void sendSms(in int format, in int messageRef, in boolean retry, in byte[] pdu); + void acknowledgeSms(in int messageRef, in int result); + int getSmsFormat(); +}
\ No newline at end of file diff --git a/telephony/java/com/android/ims/internal/ISmsListener.aidl b/telephony/java/com/android/ims/internal/ISmsListener.aidl new file mode 100644 index 000000000000..1266f040885a --- /dev/null +++ b/telephony/java/com/android/ims/internal/ISmsListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 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.ims.internal; + +/** + * See SmsFeature for more information. + * {@hide} + */ +interface ISmsListener { + void setSentSmsResult(in int messageRef, in int result); + void setSentSmsStatusReport(in int format, in byte[] pdu); + void deliverSms(in int format, in byte[] pdu); +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java index 5f2e561b687f..d27a75815980 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java @@ -18,6 +18,7 @@ package com.android.internal.telephony.cdma.sms; import android.util.SparseBooleanArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.SmsAddress; import com.android.internal.telephony.cdma.sms.UserData; import com.android.internal.util.HexDump; @@ -113,8 +114,8 @@ public class CdmaSmsAddress extends SmsAddress { * share code and logic with GSM. Also, gather all DTMF/BCD * processing code in one place. */ - - private static byte[] parseToDtmf(String address) { + @VisibleForTesting + public static byte[] parseToDtmf(String address) { int digits = address.length(); byte[] result = new byte[digits]; for (int i = 0; i < digits; i++) { @@ -196,33 +197,46 @@ public class CdmaSmsAddress extends SmsAddress { public static CdmaSmsAddress parse(String address) { CdmaSmsAddress addr = new CdmaSmsAddress(); addr.address = address; - addr.ton = CdmaSmsAddress.TON_UNKNOWN; - byte[] origBytes = null; + addr.ton = TON_UNKNOWN; + addr.digitMode = DIGIT_MODE_4BIT_DTMF; + addr.numberPlan = NUMBERING_PLAN_UNKNOWN; + addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK; + + byte[] origBytes; String filteredAddr = filterNumericSugar(address); - if (filteredAddr != null) { - origBytes = parseToDtmf(filteredAddr); - } - if (origBytes != null) { - addr.digitMode = DIGIT_MODE_4BIT_DTMF; - addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK; - if (address.indexOf('+') != -1) { - addr.ton = TON_INTERNATIONAL_OR_IP; - } - } else { - filteredAddr = filterWhitespace(address); - origBytes = UserData.stringToAscii(filteredAddr); - if (origBytes == null) { - return null; - } + if (address.contains("+") || filteredAddr == null) { + // 3GPP2 C.S0015-B section 3.4.3.3 Address Parameters + // NUMBER_MODE should set to 1 for network address and email address. addr.digitMode = DIGIT_MODE_8BIT_CHAR; addr.numberMode = NUMBER_MODE_DATA_NETWORK; - if (address.indexOf('@') != -1) { + filteredAddr = filterWhitespace(address); + + if (address.contains("@")) { + // This is an email address addr.ton = TON_NATIONAL_OR_EMAIL; + } else if (address.contains("+") && filterNumericSugar(address) != null) { + // This is an international number + // 3GPP2 C.S0015-B section 3.4.3.3 Address Parameters + // digit mode is set to 1 and number mode is set to 0, type of number should set + // to the value correspond to the value in 3GPP2 C.S005-D, table2.7.1.3.2.4-2 + addr.ton = TON_INTERNATIONAL_OR_IP; + addr.numberPlan = NUMBERING_PLAN_ISDN_TELEPHONY; + addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK; + filteredAddr = filterNumericSugar(address); } + + origBytes = UserData.stringToAscii(filteredAddr); + } else { + // The address is not an international number and it only contains digit and *# + origBytes = parseToDtmf(filteredAddr); + } + + if (origBytes == null) { + return null; } + addr.origBytes = origBytes; addr.numberOfDigits = origBytes.length; return addr; } - } diff --git a/test-base/Android.mk b/test-base/Android.mk index 9fc70f397263..6a1ac9eac3c9 100644 --- a/test-base/Android.mk +++ b/test-base/Android.mk @@ -46,20 +46,6 @@ LOCAL_JAVA_LIBRARIES := core-oj core-libart framework include $(BUILD_JAVA_LIBRARY) -# Build the repackaged-legacy-test library -# ======================================== -# This contains repackaged versions of the classes from legacy-test. -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_MODULE := repackaged-legacy-test -LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework -LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt - -include $(BUILD_STATIC_JAVA_LIBRARY) - # Build the repackaged.android.test.base library # ============================================== # This contains repackaged versions of the classes from legacy-test. diff --git a/test-mock/Android.mk b/test-mock/Android.mk index e4af17cbd711..2c079555877b 100644 --- a/test-mock/Android.mk +++ b/test-mock/Android.mk @@ -24,7 +24,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test +LOCAL_JAVA_LIBRARIES := core-oj core-libart framework LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java index 7a2344317223..90a373a9c11e 100644 --- a/tests/net/java/com/android/internal/util/RingBufferTest.java +++ b/tests/net/java/com/android/internal/util/RingBufferTest.java @@ -17,6 +17,7 @@ package com.android.internal.util; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import android.support.test.filters.SmallTest; @@ -129,6 +130,55 @@ public class RingBufferTest { assertArraysEqual(expected2, buffer.toArray()); } + @Test + public void testGetNextSlot() { + int capacity = 100; + RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity); + + final DummyClass1[] actual = new DummyClass1[capacity]; + final DummyClass1[] expected = new DummyClass1[capacity]; + for (int i = 0; i < capacity; ++i) { + final DummyClass1 obj = buffer.getNextSlot(); + obj.x = capacity * i; + actual[i] = obj; + expected[i] = new DummyClass1(); + expected[i].x = capacity * i; + } + assertArraysEqual(expected, buffer.toArray()); + + for (int i = 0; i < capacity; ++i) { + if (actual[i] != buffer.getNextSlot()) { + fail("getNextSlot() should re-use objects if available"); + } + } + + RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity); + assertNull("getNextSlot() should return null if the object can't be initiated " + + "(No nullary constructor)", buffer2.getNextSlot()); + + RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity); + assertNull("getNextSlot() should return null if the object can't be initiated " + + "(Inaccessible class)", buffer3.getNextSlot()); + } + + public static final class DummyClass1 { + int x; + + public boolean equals(Object o) { + if (o instanceof DummyClass1) { + final DummyClass1 other = (DummyClass1) o; + return other.x == this.x; + } + return false; + } + } + + public static final class DummyClass2 { + public DummyClass2(int x) {} + } + + private static final class DummyClass3 {} + static <T> void assertArraysEqual(T[] expected, T[] got) { if (expected.length != got.length) { fail(Arrays.toString(expected) + " and " + Arrays.toString(got) diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index de4fb736758c..eaaefd5e099a 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -346,30 +346,15 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, return true; } -class FullyQualifiedClassNameVisitor : public xml::Visitor { - public: - using xml::Visitor::Visit; - - explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : package_(package) {} - - void Visit(xml::Element* el) override { - for (xml::Attribute& attr : el->attributes) { - if (attr.namespace_uri == xml::kSchemaAndroid && - class_attributes_.find(attr.name) != class_attributes_.end()) { - if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package_, attr.value)) { - attr.value = std::move(new_value.value()); - } - } +static void FullyQualifyClassName(const StringPiece& package, const StringPiece& attr_ns, + const StringPiece& attr_name, xml::Element* el) { + xml::Attribute* attr = el->FindAttribute(attr_ns, attr_name); + if (attr != nullptr) { + if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package, attr->value)) { + attr->value = std::move(new_value.value()); } - - // Super implementation to iterate over the children. - xml::Visitor::Visit(el); } - - private: - StringPiece package_; - std::unordered_set<StringPiece> class_attributes_ = {"name"}; -}; +} static bool RenameManifestPackage(const StringPiece& package_override, xml::Element* manifest_el) { xml::Attribute* attr = manifest_el->FindAttribute({}, "package"); @@ -381,8 +366,25 @@ static bool RenameManifestPackage(const StringPiece& package_override, xml::Elem std::string original_package = std::move(attr->value); attr->value = package_override.to_string(); - FullyQualifiedClassNameVisitor visitor(original_package); - manifest_el->Accept(&visitor); + xml::Element* application_el = manifest_el->FindChild({}, "application"); + if (application_el != nullptr) { + FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", application_el); + FullyQualifyClassName(original_package, xml::kSchemaAndroid, "backupAgent", application_el); + + for (xml::Element* child_el : application_el->GetChildElements()) { + if (child_el->namespace_uri.empty()) { + if (child_el->name == "activity" || child_el->name == "activity-alias" || + child_el->name == "provider" || child_el->name == "receiver" || + child_el->name == "service") { + FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", child_el); + } + + if (child_el->name == "activity-alias") { + FullyQualifyClassName(original_package, xml::kSchemaAndroid, "targetActivity", child_el); + } + } + } + } return true; } diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index da7f410b8b08..40085eab9707 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -240,6 +240,7 @@ TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) { std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> + <uses-split android:name="feature_a" /> <application android:name=".MainApplication" text="hello"> <activity android:name=".activity.Start" /> <receiver android:name="com.google.android.Receiver" /> @@ -248,35 +249,41 @@ TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) { options); ASSERT_NE(nullptr, doc); - xml::Element* manifestEl = doc->root.get(); - ASSERT_NE(nullptr, manifestEl); + xml::Element* manifest_el = doc->root.get(); + ASSERT_NE(nullptr, manifest_el); xml::Attribute* attr = nullptr; - attr = manifestEl->FindAttribute({}, "package"); + attr = manifest_el->FindAttribute({}, "package"); ASSERT_NE(nullptr, attr); EXPECT_EQ(std::string("com.android"), attr->value); - xml::Element* applicationEl = manifestEl->FindChild({}, "application"); - ASSERT_NE(nullptr, applicationEl); + xml::Element* uses_split_el = manifest_el->FindChild({}, "uses-split"); + ASSERT_NE(nullptr, uses_split_el); + attr = uses_split_el->FindAttribute(xml::kSchemaAndroid, "name"); + ASSERT_NE(nullptr, attr); + EXPECT_EQ(std::string("feature_a"), attr->value); + + xml::Element* application_el = manifest_el->FindChild({}, "application"); + ASSERT_NE(nullptr, application_el); - attr = applicationEl->FindAttribute(xml::kSchemaAndroid, "name"); + attr = application_el->FindAttribute(xml::kSchemaAndroid, "name"); ASSERT_NE(nullptr, attr); EXPECT_EQ(std::string("android.MainApplication"), attr->value); - attr = applicationEl->FindAttribute({}, "text"); + attr = application_el->FindAttribute({}, "text"); ASSERT_NE(nullptr, attr); EXPECT_EQ(std::string("hello"), attr->value); xml::Element* el; - el = applicationEl->FindChild({}, "activity"); + el = application_el->FindChild({}, "activity"); ASSERT_NE(nullptr, el); attr = el->FindAttribute(xml::kSchemaAndroid, "name"); ASSERT_NE(nullptr, el); EXPECT_EQ(std::string("android.activity.Start"), attr->value); - el = applicationEl->FindChild({}, "receiver"); + el = application_el->FindChild({}, "receiver"); ASSERT_NE(nullptr, el); attr = el->FindAttribute(xml::kSchemaAndroid, "name"); |